mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-30 23:51:03 +01:00 
			
		
		
		
	OIDC: Fetch UserInfo to get EmailVerified if necessary (#2493)
This commit is contained in:
		
							parent
							
								
									badbb68217
								
							
						
					
					
						commit
						b5953d689c
					
				| @ -83,6 +83,10 @@ The new policy can be used by setting the environment variable | |||||||
|   - It is now possible to inspect running goroutines and take profiles |   - It is now possible to inspect running goroutines and take profiles | ||||||
|   - View of config, policy, filter, ssh policy per node, connected nodes and |   - View of config, policy, filter, ssh policy per node, connected nodes and | ||||||
|     DERPmap |     DERPmap | ||||||
|  | - OIDC: Fetch UserInfo to get EmailVerified if necessary | ||||||
|  |   [#2493](https://github.com/juanfont/headscale/pull/2493) | ||||||
|  |   - If a OIDC provider doesn't include the `email_verified` claim in its ID | ||||||
|  |     tokens, Headscale will attempt to get it from the UserInfo endpoint. | ||||||
| 
 | 
 | ||||||
| ## 0.25.1 (2025-02-25) | ## 0.25.1 (2025-02-25) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -234,7 +234,14 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	idToken, err := a.extractIDToken(req.Context(), code, state) | 	oauth2Token, err := a.getOauth2Token(req.Context(), code, state) | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		httpError(writer, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	idToken, err := a.extractIDToken(req.Context(), oauth2Token) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		httpError(writer, err) | 		httpError(writer, err) | ||||||
| 		return | 		return | ||||||
| @ -273,6 +280,16 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// If EmailVerified is missing, we can try to get it from UserInfo | ||||||
|  | 	if !claims.EmailVerified { | ||||||
|  | 		var userinfo *oidc.UserInfo | ||||||
|  | 		userinfo, err = a.oidcProvider.UserInfo(req.Context(), oauth2.StaticTokenSource(oauth2Token)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			util.LogErr(err, "could not get userinfo; email cannot be verified") | ||||||
|  | 		} | ||||||
|  | 		claims.EmailVerified = types.FlexibleBoolean(userinfo.EmailVerified) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	user, err := a.createOrUpdateUserFromClaim(&claims) | 	user, err := a.createOrUpdateUserFromClaim(&claims) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		httpError(writer, err) | 		httpError(writer, err) | ||||||
| @ -333,13 +350,12 @@ func extractCodeAndStateParamFromRequest( | |||||||
| 	return code, state, nil | 	return code, state, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // extractIDToken takes the code parameter from the callback | // getOauth2Token exchanges the code from the callback for an oauth2 token. | ||||||
| // and extracts the ID token from the oauth2 token. | func (a *AuthProviderOIDC) getOauth2Token( | ||||||
| func (a *AuthProviderOIDC) extractIDToken( |  | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	code string, | 	code string, | ||||||
| 	state string, | 	state string, | ||||||
| ) (*oidc.IDToken, error) { | ) (*oauth2.Token, error) { | ||||||
| 	var exchangeOpts []oauth2.AuthCodeOption | 	var exchangeOpts []oauth2.AuthCodeOption | ||||||
| 
 | 
 | ||||||
| 	if a.cfg.PKCE.Enabled { | 	if a.cfg.PKCE.Enabled { | ||||||
| @ -356,7 +372,14 @@ func (a *AuthProviderOIDC) extractIDToken( | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, NewHTTPError(http.StatusForbidden, "invalid code", fmt.Errorf("could not exchange code for token: %w", err)) | 		return nil, NewHTTPError(http.StatusForbidden, "invalid code", fmt.Errorf("could not exchange code for token: %w", err)) | ||||||
| 	} | 	} | ||||||
|  | 	return oauth2Token, err | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | // extractIDToken extracts the ID token from the oauth2 token. | ||||||
|  | func (a *AuthProviderOIDC) extractIDToken( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	oauth2Token *oauth2.Token, | ||||||
|  | ) (*oidc.IDToken, error) { | ||||||
| 	rawIDToken, ok := oauth2Token.Extra("id_token").(string) | 	rawIDToken, ok := oauth2Token.Extra("id_token").(string) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, NewHTTPError(http.StatusBadRequest, "no id_token", errNoOIDCIDToken) | 		return nil, NewHTTPError(http.StatusBadRequest, "no id_token", errNoOIDCIDToken) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user