Unverified Commit 6ee8de53 authored by Mechiel Lukkien's avatar Mechiel Lukkien Committed by GitHub
Browse files

Fetch OpenID Connect claims with UserInfo call, not id_token, and pass claims...

Fetch OpenID Connect claims with UserInfo call, not id_token, and pass claims to registration callback url (#613)
parent 79549e8f
package external
import (
"encoding/json"
"fmt"
"net/http"
......@@ -320,6 +321,11 @@ func (a *InternalAPI) OpenIDConnectLogin(ctx context.Context, req *pb.OpenIDConn
if err != nil {
return nil, helpers.ErrToRPCError(err)
}
// fetch user again because the provisioning callback url may have updated the user.
user, err = storage.GetUser(ctx, storage.DB(), user.ID)
if err != nil {
return nil, helpers.ErrToRPCError(err)
}
} else {
return nil, helpers.ErrToRPCError(err)
}
......@@ -412,7 +418,7 @@ func (a *InternalAPI) createAndProvisionUser(ctx context.Context, user oidc.User
return u, nil
}
if err := a.provisionUser(ctx, u); err != nil {
if err := a.provisionUser(ctx, u, user); err != nil {
if err := storage.DeleteUser(ctx, storage.DB(), u.ID); err != nil {
return storage.User{}, errors.Wrap(err, "delete user error after failed user provisioning")
}
......@@ -425,13 +431,18 @@ func (a *InternalAPI) createAndProvisionUser(ctx context.Context, user oidc.User
return u, nil
}
func (a *InternalAPI) provisionUser(ctx context.Context, u storage.User) error {
func (a *InternalAPI) provisionUser(ctx context.Context, u storage.User, oidcUser oidc.User) error {
req, err := http.NewRequestWithContext(ctx, "POST", registrationCallbackURL, nil)
if err != nil {
return errors.Wrap(err, "new request error")
}
q := req.URL.Query()
q.Add("user_id", fmt.Sprintf("%d", u.ID))
claims, err := json.Marshal(oidcUser.UserInfoClaims)
if err != nil {
return errors.Wrap(err, "marshal claims error")
}
q.Add("oidc_claims", string(claims))
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
......
......@@ -33,10 +33,11 @@ var (
// User defines an OpenID Connect user object.
type User struct {
ExternalID string `json:"sub"`
Name string `json:"name"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
ExternalID string `json:"sub"`
Name string `json:"name"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}
// Setup configured the OpenID Connect endpoint handlers.
......@@ -190,15 +191,32 @@ func GetUser(ctx context.Context, code string, state string) (User, error) {
ClientID: clientID,
}
idToken, err := auth.provider.Verifier(oidcConfig).Verify(ctx, rawIDToken)
if err != nil {
if _, err := auth.provider.Verifier(oidcConfig).Verify(ctx, rawIDToken); err != nil {
return User{}, errors.Wrap(err, "verify id token error")
}
var user User
if err := idToken.Claims(&user); err != nil {
// Request the claims through a UserInfo call to the OIDC server.
// We don't read claims from the "id_token" because most OIDC servers don't include
// claims in id_token by default. We would have to request claims to be included in
// id_token explicitly. But then servers don't have to implement/support that
// request. The UserInfo call does always include the claims.
userInfo, err := auth.provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
if err != nil {
return User{}, errors.Wrap(err, "get userInfo error")
}
// Parse the well-known claims into user.
if err := userInfo.Claims(&user); err != nil {
return User{}, errors.Wrap(err, "get userInfo claims for user error")
}
// And also parse the claims in a map, so we can pass them on when calling the registration URL later on.
user.UserInfoClaims = map[string]interface{}{}
if err := userInfo.Claims(&user.UserInfoClaims); err != nil {
return User{}, errors.Wrap(err, "get userInfo claims for user claims map error")
}
return user, nil
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment