mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
feat: add user metadata scope and claim (#2268)
* feat: add user metadata scope and claime * docs: scope * docs: metadata base 64 encoded * docs: metadata base 64 encoded * docs: metadata base 64 encoded
This commit is contained in:
parent
1da68420bc
commit
5b4c64d740
@ -28,5 +28,6 @@ In addition to the standard compliant scopes we utilize the following scopes.
|
||||
| urn:zitadel:iam:org:domain:primary:{domainname} | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization. If the organization does not exist a failure is displayed |
|
||||
| urn:zitadel:iam:role:{rolename} | | |
|
||||
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | ZITADEL's Project id is `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested projectid will be added to the audience of the access and id token |
|
||||
| urn:zitadel:iam:user:metadata | `urn:zitadel:iam:user:metadata` | By adding this scope, the metadata of the user will be included in the token. The values are base64 encoded. |
|
||||
|
||||
> If access to ZITADEL's API's is needed with a service user the scope `urn:zitadel:iam:org:project:id:69234237810729019:aud` needs to be used with the JWT Profile request
|
||||
|
@ -2,6 +2,7 @@ package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
@ -22,14 +23,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
scopeOpenID = "openid"
|
||||
scopeProfile = "profile"
|
||||
scopeEmail = "email"
|
||||
scopePhone = "phone"
|
||||
scopeAddress = "address"
|
||||
|
||||
ScopeProjectRolePrefix = "urn:zitadel:iam:org:project:role:"
|
||||
ClaimProjectRoles = "urn:zitadel:iam:org:project:roles"
|
||||
ScopeUserMetaData = "urn:zitadel:iam:user:metadata"
|
||||
ClaimUserMetaData = ScopeUserMetaData
|
||||
|
||||
oidcCtx = "oidc"
|
||||
)
|
||||
@ -186,6 +183,13 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use
|
||||
}
|
||||
}
|
||||
}
|
||||
userMetaData, err := o.assertUserMetaData(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(userMetaData) > 0 {
|
||||
userInfo.AppendClaims(ClaimUserMetaData, userMetaData)
|
||||
}
|
||||
|
||||
if len(roles) == 0 || applicationID == "" {
|
||||
return nil
|
||||
@ -197,7 +201,6 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use
|
||||
if len(projectRoles) > 0 {
|
||||
userInfo.AppendClaims(ClaimProjectRoles, projectRoles)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -243,6 +246,13 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
|
||||
if len(projectRoles) > 0 {
|
||||
claims = appendClaim(claims, ClaimProjectRoles, projectRoles)
|
||||
}
|
||||
userMetaData, err := o.assertUserMetaData(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(userMetaData) > 0 {
|
||||
claims = appendClaim(claims, ClaimUserMetaData, userMetaData)
|
||||
}
|
||||
return claims, err
|
||||
}
|
||||
|
||||
@ -264,6 +274,19 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
||||
return projectRoles, nil
|
||||
}
|
||||
|
||||
func (o *OPStorage) assertUserMetaData(ctx context.Context, userID string) (map[string]string, error) {
|
||||
metaData, err := o.repo.SearchUserMetadata(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userMetaData := make(map[string]string)
|
||||
for _, md := range metaData.Result {
|
||||
userMetaData[md.Key] = base64.RawURLEncoding.EncodeToString(md.Value)
|
||||
}
|
||||
return userMetaData, nil
|
||||
}
|
||||
|
||||
func checkGrantedRoles(roles map[string]map[string]string, grant *grant_model.UserGrantView, requestedRole string) {
|
||||
for _, grantedRole := range grant.RoleKeys {
|
||||
if requestedRole == grantedRole {
|
||||
|
@ -106,6 +106,9 @@ func (c *Client) IsScopeAllowed(scope string) bool {
|
||||
if strings.HasPrefix(scope, authreq_model.ProjectIDScope) {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(scope, ScopeUserMetaData) {
|
||||
return true
|
||||
}
|
||||
for _, allowedScope := range c.allowedScopes {
|
||||
if scope == allowedScope {
|
||||
return true
|
||||
|
@ -307,16 +307,27 @@ func (repo *UserRepo) GetMyMetadataByKey(ctx context.Context, key string) (*doma
|
||||
return iam_model.MetadataViewToDomain(data), nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) {
|
||||
req := new(domain.MetadataSearchRequest)
|
||||
return repo.searchUserMetadata(userID, "", req)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) SearchMyMetadata(ctx context.Context, req *domain.MetadataSearchRequest) (*domain.MetadataSearchResponse, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := req.EnsureLimit(repo.SearchLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.searchUserMetadata(ctxData.UserID, ctxData.ResourceOwner, req)
|
||||
}
|
||||
|
||||
func (repo *UserRepo) searchUserMetadata(userID, resourceOwner string, req *domain.MetadataSearchRequest) (*domain.MetadataSearchResponse, error) {
|
||||
sequence, sequenceErr := repo.View.GetLatestUserSequence()
|
||||
logging.Log("EVENT-N9fsd").OnError(sequenceErr).Warn("could not read latest user sequence")
|
||||
req.AppendAggregateIDQuery(ctxData.UserID)
|
||||
req.AppendResourceOwnerQuery(ctxData.ResourceOwner)
|
||||
req.AppendAggregateIDQuery(userID)
|
||||
if resourceOwner != "" {
|
||||
req.AppendResourceOwnerQuery(resourceOwner)
|
||||
}
|
||||
metadata, count, err := repo.View.SearchMetadata(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -21,6 +21,8 @@ type UserRepository interface {
|
||||
MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error)
|
||||
|
||||
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
|
||||
|
||||
SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error)
|
||||
}
|
||||
|
||||
type myUserRepo interface {
|
||||
|
Loading…
Reference in New Issue
Block a user