Skip to content

Commit

Permalink
feat: add GOTRUE_<PROVIDER>_SKIP_NONCE_CHECK to skip nonce checks i…
Browse files Browse the repository at this point in the history
…n ODIC flow (supabase#1264)

It appears that in certain client libraries that deal with the OIDC
authentication flow, such as [this one for React Native on
iOS](google/GoogleSignIn-iOS#244), the clients
are unable to extract the nonce that is generated randomly by the
library.

This option allows to temporarily drop the enforcement at the GoTrue
level when performing the OIDC flow. This does remove an important
security barrier, which could potentially allow "stolen" ID tokens to be
used on third-party services (that have opted in to this configuration)
however in the interest of flexibility and broad platform support the
option is being added.
  • Loading branch information
hf authored and hoeseong19 committed Oct 16, 2023
1 parent 81694a4 commit 6a0fade
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 22 deletions.
34 changes: 18 additions & 16 deletions internal/api/token_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type IdTokenGrantParams struct {
Issuer string `json:"issuer"`
}

func (p *IdTokenGrantParams) getProvider(ctx context.Context, config *conf.GlobalConfiguration, r *http.Request) (*oidc.Provider, string, []string, error) {
func (p *IdTokenGrantParams) getProvider(ctx context.Context, config *conf.GlobalConfiguration, r *http.Request) (*oidc.Provider, *conf.OAuthProviderConfiguration, string, []string, error) {
log := observability.GetLogEntry(r)

var cfg *conf.OAuthProviderConfiguration
Expand Down Expand Up @@ -84,20 +84,20 @@ func (p *IdTokenGrantParams) getProvider(ctx context.Context, config *conf.Globa
}

if !allowed {
return nil, "", nil, badRequestError(fmt.Sprintf("Custom OIDC provider %q not allowed", p.Issuer))
return nil, nil, "", nil, badRequestError(fmt.Sprintf("Custom OIDC provider %q not allowed", p.Issuer))
}
}

if cfg != nil && !cfg.Enabled {
return nil, "", nil, badRequestError(fmt.Sprintf("Provider (issuer %q) is not enabled", issuer))
return nil, nil, "", nil, badRequestError(fmt.Sprintf("Provider (issuer %q) is not enabled", issuer))
}

oidcProvider, err := oidc.NewProvider(ctx, issuer)
if err != nil {
return nil, "", nil, err
return nil, nil, "", nil, err
}

return oidcProvider, providerType, acceptableClientIDs, nil
return oidcProvider, cfg, providerType, acceptableClientIDs, nil
}

// IdTokenGrant implements the id_token grant type flow
Expand Down Expand Up @@ -126,7 +126,7 @@ func (a *API) IdTokenGrant(ctx context.Context, w http.ResponseWriter, r *http.R
return oauthError("invalid request", "provider or client_id and issuer required")
}

oidcProvider, providerType, acceptableClientIDs, err := params.getProvider(ctx, config, r)
oidcProvider, oauthConfig, providerType, acceptableClientIDs, err := params.getProvider(ctx, config, r)
if err != nil {
return err
}
Expand Down Expand Up @@ -165,16 +165,18 @@ func (a *API) IdTokenGrant(ctx context.Context, w http.ResponseWriter, r *http.R
return oauthError("invalid request", "Unacceptable audience in id_token")
}

tokenHasNonce := idToken.Nonce != ""
paramsHasNonce := params.Nonce != ""

if tokenHasNonce != paramsHasNonce {
return oauthError("invalid request", "Passed nonce and nonce in id_token should either both exist or not.")
} else if tokenHasNonce && paramsHasNonce {
// verify nonce to mitigate replay attacks
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(params.Nonce)))
if hash != idToken.Nonce {
return oauthError("invalid nonce", "Nonces mismatch")
if !oauthConfig.SkipNonceCheck {
tokenHasNonce := idToken.Nonce != ""
paramsHasNonce := params.Nonce != ""

if tokenHasNonce != paramsHasNonce {
return oauthError("invalid request", "Passed nonce and nonce in id_token should either both exist or not.")
} else if tokenHasNonce && paramsHasNonce {
// verify nonce to mitigate replay attacks
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(params.Nonce)))
if hash != idToken.Nonce {
return oauthError("invalid nonce", "Nonces mismatch")
}
}
}

Expand Down
13 changes: 7 additions & 6 deletions internal/conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ func (t *Time) UnmarshalText(text []byte) error {

// OAuthProviderConfiguration holds all config related to external account providers.
type OAuthProviderConfiguration struct {
ClientID []string `json:"client_id" split_words:"true"`
Secret string `json:"secret"`
RedirectURI string `json:"redirect_uri" split_words:"true"`
URL string `json:"url"`
ApiURL string `json:"api_url" split_words:"true"`
Enabled bool `json:"enabled"`
ClientID []string `json:"client_id" split_words:"true"`
Secret string `json:"secret"`
RedirectURI string `json:"redirect_uri" split_words:"true"`
URL string `json:"url"`
ApiURL string `json:"api_url" split_words:"true"`
Enabled bool `json:"enabled"`
SkipNonceCheck bool `json:"skip_nonce_check" split_words:"true"`
}

type EmailProviderConfiguration struct {
Expand Down

0 comments on commit 6a0fade

Please sign in to comment.