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: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: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: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
|
> 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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/caos/oidc/pkg/oidc"
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
@ -22,14 +23,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
scopeOpenID = "openid"
|
|
||||||
scopeProfile = "profile"
|
|
||||||
scopeEmail = "email"
|
|
||||||
scopePhone = "phone"
|
|
||||||
scopeAddress = "address"
|
|
||||||
|
|
||||||
ScopeProjectRolePrefix = "urn:zitadel:iam:org:project:role:"
|
ScopeProjectRolePrefix = "urn:zitadel:iam:org:project:role:"
|
||||||
ClaimProjectRoles = "urn:zitadel:iam:org:project:roles"
|
ClaimProjectRoles = "urn:zitadel:iam:org:project:roles"
|
||||||
|
ScopeUserMetaData = "urn:zitadel:iam:user:metadata"
|
||||||
|
ClaimUserMetaData = ScopeUserMetaData
|
||||||
|
|
||||||
oidcCtx = "oidc"
|
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 == "" {
|
if len(roles) == 0 || applicationID == "" {
|
||||||
return nil
|
return nil
|
||||||
@ -197,7 +201,6 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use
|
|||||||
if len(projectRoles) > 0 {
|
if len(projectRoles) > 0 {
|
||||||
userInfo.AppendClaims(ClaimProjectRoles, projectRoles)
|
userInfo.AppendClaims(ClaimProjectRoles, projectRoles)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,6 +246,13 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
|
|||||||
if len(projectRoles) > 0 {
|
if len(projectRoles) > 0 {
|
||||||
claims = appendClaim(claims, ClaimProjectRoles, projectRoles)
|
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
|
return claims, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +274,19 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
|||||||
return projectRoles, nil
|
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) {
|
func checkGrantedRoles(roles map[string]map[string]string, grant *grant_model.UserGrantView, requestedRole string) {
|
||||||
for _, grantedRole := range grant.RoleKeys {
|
for _, grantedRole := range grant.RoleKeys {
|
||||||
if requestedRole == grantedRole {
|
if requestedRole == grantedRole {
|
||||||
|
@ -106,6 +106,9 @@ func (c *Client) IsScopeAllowed(scope string) bool {
|
|||||||
if strings.HasPrefix(scope, authreq_model.ProjectIDScope) {
|
if strings.HasPrefix(scope, authreq_model.ProjectIDScope) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(scope, ScopeUserMetaData) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
for _, allowedScope := range c.allowedScopes {
|
for _, allowedScope := range c.allowedScopes {
|
||||||
if scope == allowedScope {
|
if scope == allowedScope {
|
||||||
return true
|
return true
|
||||||
|
@ -307,16 +307,27 @@ func (repo *UserRepo) GetMyMetadataByKey(ctx context.Context, key string) (*doma
|
|||||||
return iam_model.MetadataViewToDomain(data), nil
|
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) {
|
func (repo *UserRepo) SearchMyMetadata(ctx context.Context, req *domain.MetadataSearchRequest) (*domain.MetadataSearchResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
err := req.EnsureLimit(repo.SearchLimit)
|
err := req.EnsureLimit(repo.SearchLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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()
|
sequence, sequenceErr := repo.View.GetLatestUserSequence()
|
||||||
logging.Log("EVENT-N9fsd").OnError(sequenceErr).Warn("could not read latest user sequence")
|
logging.Log("EVENT-N9fsd").OnError(sequenceErr).Warn("could not read latest user sequence")
|
||||||
req.AppendAggregateIDQuery(ctxData.UserID)
|
req.AppendAggregateIDQuery(userID)
|
||||||
req.AppendResourceOwnerQuery(ctxData.ResourceOwner)
|
if resourceOwner != "" {
|
||||||
|
req.AppendResourceOwnerQuery(resourceOwner)
|
||||||
|
}
|
||||||
metadata, count, err := repo.View.SearchMetadata(req)
|
metadata, count, err := repo.View.SearchMetadata(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -21,6 +21,8 @@ type UserRepository interface {
|
|||||||
MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error)
|
MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error)
|
||||||
|
|
||||||
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
|
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
|
||||||
|
|
||||||
|
SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type myUserRepo interface {
|
type myUserRepo interface {
|
||||||
|
Loading…
Reference in New Issue
Block a user