Add groups scope/claim to OIDC/OAuth2 Provider (#17367)

* Add groups scope/claim to OICD/OAuth2

Add support for groups claim as part of the OIDC/OAuth2 flow.
Groups is a list of "org" and "org:team" strings to allow clients to
authorize based on the groups a user is part of.

Signed-off-by: Nico Schieder <code@nico-schieder.de>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Nico Schieder 2021-10-22 11:19:24 +02:00 committed by GitHub
parent af96286f22
commit 870f5fbc41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 7 deletions

View file

@ -207,6 +207,17 @@ func newAccessTokenResponse(grant *login.OAuth2Grant, serverKey, clientKey oauth
idToken.Email = user.Email
idToken.EmailVerified = user.IsActive
}
if grant.ScopeContains("groups") {
groups, err := getOAuthGroupsForUser(user)
if err != nil {
log.Error("Error getting groups: %v", err)
return nil, &AccessTokenError{
ErrorCode: AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "server error",
}
}
idToken.Groups = groups
}
signedIDToken, err = idToken.SignToken(clientKey)
if err != nil {
@ -227,11 +238,12 @@ func newAccessTokenResponse(grant *login.OAuth2Grant, serverKey, clientKey oauth
}
type userInfoResponse struct {
Sub string `json:"sub"`
Name string `json:"name"`
Username string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
Sub string `json:"sub"`
Name string `json:"name"`
Username string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
Groups []string `json:"groups"`
}
// InfoOAuth manages request for userinfo endpoint
@ -241,6 +253,7 @@ func InfoOAuth(ctx *context.Context) {
ctx.HandleText(http.StatusUnauthorized, "no valid authorization")
return
}
response := &userInfoResponse{
Sub: fmt.Sprint(ctx.User.ID),
Name: ctx.User.FullName,
@ -248,9 +261,41 @@ func InfoOAuth(ctx *context.Context) {
Email: ctx.User.Email,
Picture: ctx.User.AvatarLink(),
}
groups, err := getOAuthGroupsForUser(ctx.User)
if err != nil {
ctx.ServerError("Oauth groups for user", err)
return
}
response.Groups = groups
ctx.JSON(http.StatusOK, response)
}
// returns a list of "org" and "org:team" strings,
// that the given user is a part of.
func getOAuthGroupsForUser(user *models.User) ([]string, error) {
orgs, err := models.GetUserOrgsList(user)
if err != nil {
return nil, fmt.Errorf("GetUserOrgList: %v", err)
}
var groups []string
for _, org := range orgs {
groups = append(groups, org.Name)
if err := org.LoadTeams(); err != nil {
return nil, fmt.Errorf("LoadTeams: %v", err)
}
for _, team := range org.Teams {
if team.IsMember(user.ID) {
groups = append(groups, org.Name+":"+team.LowerName)
}
}
}
return groups, nil
}
// IntrospectOAuth introspects an oauth token
func IntrospectOAuth(ctx *context.Context) {
if ctx.User == nil {

View file

@ -83,6 +83,9 @@ type OIDCToken struct {
// Scope email
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
// Groups are generated by organization and team names
Groups []string `json:"groups,omitempty"`
}
// SignToken signs an id_token with the (symmetric) client secret key

View file

@ -18,7 +18,8 @@
"scopes_supported": [
"openid",
"profile",
"email"
"email",
"groups"
],
"claims_supported": [
"aud",
@ -34,7 +35,8 @@
"locale",
"updated_at",
"email",
"email_verified"
"email_verified",
"groups"
],
"code_challenge_methods_supported": [
"plain",