Skip to content

Commit

Permalink
fix(backend): google auth (#1022)
Browse files Browse the repository at this point in the history
- fix intermittent login issues with google
- modify sign in with google settings to use `redirect` ux mode
- modify sign in with google settings to use fedCM
- modify google auth stack to work around issues with safari
  • Loading branch information
detj authored Aug 12, 2024
1 parent b7b1c7a commit 06a35e8
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 18 deletions.
36 changes: 27 additions & 9 deletions measure-backend/measure-go/measure/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,20 +391,35 @@ func SigninGoogle(c *gin.Context) {
return
}

if authState.Nonce == "" {
if authState.Credential == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "missing credentials",
})
return
}

if authState.Nonce == "" && authState.State != "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "missing nonce parameter",
})
return
}

if authState.State == "" {
if authState.State == "" && authState.Nonce != "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "missing state parameter",
})
return
}

// Google API JavaScript client has an open issue where
// it does not send nonce or state in its authorization
// callback
// See: https://github.com/google/google-api-javascript-client/issues/843
//
// If nonce and state, both are empty, we consider it
// valid and proceed for now.

payload, err := idtoken.Validate(ctx, authState.Credential, server.Server.Config.OAuthGoogleKey)
if err != nil {
msg := "failed to validate google credentials"
Expand All @@ -426,13 +441,16 @@ func SigninGoogle(c *gin.Context) {
return
}

if payload.Claims["nonce"] != *checksum {
msg := "failed to validate nonce"
fmt.Println(msg)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"error": msg,
})
return
// Validate nonce if present
if authState.Nonce != "" {
if payload.Claims["nonce"] != *checksum {
msg := "failed to validate nonce"
fmt.Println(msg)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"error": msg,
})
return
}
}

googUser := authsession.GoogleUser{
Expand Down
30 changes: 24 additions & 6 deletions measure-web-app/app/auth/callback/google/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,36 @@ export async function POST(request: Request) {
const nonce = searchParams.get('nonce')
const state = searchParams.get('state')

if (!nonce) {
const formdata = await request.formData()
const credential = formdata.get('credential')

if (!credential) {
console.log("google login failure: no credential")
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

if (state && !nonce) {
console.log("google login failure: no nonce")
return NextResponse.redirect(errRedirectUrl, { status: 301 })
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

if (!state) {
if (nonce && !state) {
console.log("google login failure: no state")
return NextResponse.redirect(errRedirectUrl, { status: 301 })
return NextResponse.redirect(errRedirectUrl, { status: 302 })
}

const formdata = await request.formData()
const credential = formdata.get('credential')
// Google API JavaScript client has an open issue where
// it does not send nonce or state in its authorization
// callback
// See: https://github.com/google/google-api-javascript-client/issues/843
//
// If nonce and state, both are empty, we consider it
// valid and proceed for now, while keeping an eye out
// on the user agent.
if (!nonce && !state) {
const headers = Object.fromEntries(request.headers)
console.log(`google login warning: nonce and state both are missing, request possibly originated from Safari, check UA: ${headers["user-agent"]}`)
}

const res = await fetch(`${apiOrigin}/auth/google`, {
method: 'POST',
Expand Down
6 changes: 3 additions & 3 deletions measure-web-app/app/auth/login/google-sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export default function GoogleSignIn() {
<div id="g_id_onload"
data-client_id={googleClientID}
data-context="signin"
data-ux_mode="popup"
data-ux_mode="redirect"
data-nonce={hashedNonce}
data-login_uri={`${origin}/auth/callback/google?nonce=${encodeURIComponent(nonce)}&state=${encodeURIComponent(state)}`}
data-auto_select="false"
data-auto_prompt="false"
data-itp_support="true">
data-itp_support="true"
data-use_fedcm_for_prompt="true">
</div>

<div className="g_id_signin"
Expand Down

0 comments on commit 06a35e8

Please sign in to comment.