mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-13 03:24:26 +00:00
feat: add ZITADEL project id scope (#4146)
* feat: add ZITADEL project id scope * update documentation * documentation * fix scopes * change to lowercase
This commit is contained in:
parent
1b5c8677ab
commit
02d2032790
@ -6,13 +6,13 @@ ZITADEL supports the usage of scopes as way of requesting information from the I
|
||||
|
||||
## Standard Scopes
|
||||
|
||||
| Scopes | Example | Description |
|
||||
|:---------------|:-----------------|--------------------------------------------------------------------------------|
|
||||
| openid | `openid` | When using openid connect this is a mandatory scope |
|
||||
| profile | `profile` | Optional scope to request the profile of the subject |
|
||||
| email | `email` | Optional scope to request the email of the subject |
|
||||
| address | `address` | Optional scope to request the address of the subject |
|
||||
| offline_access | `offline_access` | Optional scope to request a refresh_token (only possible when using code flow) |
|
||||
| Scopes | Description |
|
||||
|:---------------|--------------------------------------------------------------------------------|
|
||||
| openid | When using openid connect this is a mandatory scope |
|
||||
| profile | Optional scope to request the profile of the subject |
|
||||
| email | Optional scope to request the email of the subject |
|
||||
| address | Optional scope to request the address of the subject |
|
||||
| offline_access | Optional scope to request a refresh_token (only possible when using code flow) |
|
||||
|
||||
## Custom Scopes
|
||||
|
||||
@ -22,14 +22,13 @@ ZITADEL supports the usage of scopes as way of requesting information from the I
|
||||
|
||||
In addition to the standard compliant scopes we utilize the following scopes.
|
||||
|
||||
| Scopes | Example | Description |
|
||||
|:-------------------------------------------------|:-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| urn:zitadel:iam:org:project:role:{rolename} | `urn:zitadel:iam:org:project:role:user` | By using this scope a client can request the claim urn:zitadel:iam:roles:rolename} to be asserted when possible. As an alternative approach you can enable all roles to be asserted from the [project](../../guides/manage/console/projects) a client belongs to. |
|
||||
| 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. |
|
||||
| urn:zitadel:iam:user:resourceowner | `urn:zitadel:iam:user:resourceowner` | By adding this scope, the resourceowner (id, name, primary_domain) of the user will be included in the token. |
|
||||
| urn:zitadel:iam:org:idp:id:{idp_id} | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |
|
||||
|
||||
> 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
|
||||
| Scopes | Example | Description |
|
||||
|:--------------------------------------------------|:-------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `urn:zitadel:iam:org:project:role:{rolename}` | `urn:zitadel:iam:org:project:role:user` | By using this scope a client can request the claim urn:zitadel:iam:roles:rolename} to be asserted when possible. As an alternative approach you can enable all roles to be asserted from the [project](../../guides/manage/console/projects) a client belongs to. |
|
||||
| `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` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested projectid will be added to the audience of the access token |
|
||||
| `urn:zitadel:iam:org:project:id:zitadel:aud` | `urn:zitadel:iam:org:project:id:zitadel:aud` | By adding this scope, the ZITADEL project ID will be added to the audience of the access 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. |
|
||||
| `urn:zitadel:iam:user:resourceowner` | `urn:zitadel:iam:user:resourceowner` | By adding this scope, the resourceowner (id, name, primary_domain) of the user will be included in the token. |
|
||||
| `urn:zitadel:iam:org:idp:id:{idp_id}` | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |
|
||||
|
@ -31,11 +31,14 @@ go get github.com/zitadel/zitadel-go/v2
|
||||
### Create example client
|
||||
|
||||
Create a new go file with the content below. This will create a client for the management api and call its `GetMyOrg` function.
|
||||
The SDK will make sure you will have access to the API by retrieving a Bearer Token using JWT Profile with the provided scopes (`openid` and `urn:zitadel:iam:org:project:id:{projectID}:aud`).
|
||||
Make sure to fill the vars `issuer`, `api`, `projectID `and `orgID`
|
||||
The SDK will make sure you will have access to the API by retrieving a Bearer Token using JWT Profile with the provided scopes (`openid` and `urn:zitadel:iam:org:project:id:zitadel:aud`).
|
||||
Make sure to fill the vars `issuer` and `api`.
|
||||
|
||||
The issuer and api is the domain of your instance you can find it on the instance detail in the ZITADEL Cloud Customer Portal or in the ZITADEL Console.
|
||||
The projectID you will find in the ZITADEL project in the first organization of your instance and the orgID on the first organization.
|
||||
|
||||
:::note
|
||||
The issuer will require the protocol (`https://` and `http://`) and you will only have to specify a port if they're not default (443 for https and 80 for http). The API will always require a port, but no protocol.
|
||||
:::
|
||||
|
||||
```go
|
||||
package main
|
||||
@ -54,43 +57,44 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
issuer = flag.String("issuer", "", "issuer of your ZITADEL instance (in the form: https://<instance>.zitadel.cloud or https://<yourdomain>)")
|
||||
api = flag.String("api", "", "gRPC endpoint of your ZITADEL instance (in the form: <instance>.zitadel.cloud:443 or <yourdomain>:443)")
|
||||
projectID = flag.String("projectID", "", "ZITADEL projectID in your instance")
|
||||
orgID = flag.String("orgID", "", "orgID to set for overwrite example")
|
||||
issuer = flag.String("issuer", "", "issuer of your ZITADEL instance (in the form: https://<instance>.zitadel.cloud or https://<yourdomain>)")
|
||||
api = flag.String("api", "", "gRPC endpoint of your ZITADEL instance (in the form: <instance>.zitadel.cloud:443 or <yourdomain>:443)")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
flag.Parse()
|
||||
|
||||
client, err := management.NewClient(
|
||||
*issuer,
|
||||
*api,
|
||||
[]string{oidc.ScopeOpenID, zitadel.ScopeProjectID(*projectID)},
|
||||
//create a client for the management api providing:
|
||||
//- issuer (e.g. https://acme-dtfhdg.zitadel.cloud)
|
||||
//- api (e.g. acme-dtfhdg.zitadel.cloud:443)
|
||||
//- scopes (including the ZITADEL project ID),
|
||||
//- a JWT Profile token source (e.g. path to your key json), if not provided, the file will be read from the path set in env var ZITADEL_KEY_PATH
|
||||
client, err := management.NewClient(
|
||||
*issuer,
|
||||
*api,
|
||||
[]string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalln("could not create client", err)
|
||||
}
|
||||
defer func() {
|
||||
err := client.Connection.Close()
|
||||
if err != nil {
|
||||
log.Println("could not close grpc connection", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
resp, err := client.GetMyOrg(ctx, &pb.GetMyOrgRequest{})
|
||||
if err != nil {
|
||||
log.Fatalln("call failed: ", err)
|
||||
}
|
||||
log.Printf("%s was created on: %s", resp.Org.Name, resp.Org.Details.CreationDate.AsTime())
|
||||
|
||||
respOverwrite, err := client.GetMyOrg(middleware.SetOrgID(ctx, *orgID), &pb.GetMyOrgRequest{})
|
||||
if err != nil {
|
||||
log.Fatalln("call failed: ", err)
|
||||
}
|
||||
log.Printf("%s was created on: %s", respOverwrite.Org.Name, respOverwrite.Org.Details.CreationDate.AsTime())
|
||||
if err != nil {
|
||||
log.Fatalln("could not create client", err)
|
||||
}
|
||||
defer func() {
|
||||
err := client.Connection.Close()
|
||||
if err != nil {
|
||||
log.Println("could not close grpc connection", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
//call ZITADEL and print the name and creation date of your organisation
|
||||
//the call was successful if no error occurred
|
||||
resp, err := client.GetMyOrg(ctx, &pb.GetMyOrgRequest{})
|
||||
if err != nil {
|
||||
log.Fatalln("call failed: ", err)
|
||||
}
|
||||
log.Printf("%s was created on: %s", resp.Org.Name, resp.Org.Details.CreationDate.AsTime())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Key JSON
|
||||
|
@ -41,14 +41,14 @@ With the encoded JWT from the prior step, you will need to craft a POST request
|
||||
To access the ZITADEL APIs you need the ZITADEL Project ID in the audience of your token.
|
||||
This is possible by sending a custom scope for the audience. More about [Custom Scopes](../../apis/openidoauth/scopes)
|
||||
|
||||
Use the scope `urn:zitadel:iam:org:project:id:{projectid}:aud` to include the project id in your audience
|
||||
Use the scope `urn:zitadel:iam:org:project:id:zitadel:aud` to include the ZITADEL project id in your audience
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/token \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
|
||||
--data scope='openid profile email urn:zitadel:iam:org:project:id:69234237810729019:aud' \
|
||||
--data scope='openid profile email urn:zitadel:iam:org:project:id:zitadel:aud' \
|
||||
--data assertion=eyJ0eXAiOiJKV1QiL...
|
||||
```
|
||||
|
||||
@ -75,7 +75,7 @@ With this token you are allowed to access the [ZITADEL APIs](../../apis/introduc
|
||||
|
||||
* Grant a user for ZITADEL
|
||||
* Because there is no interactive logon, you need to use a JWT signed with your private key to authorize the user
|
||||
* With a custom scope (`urn:zitadel:iam:org:project:id:{projectid}:aud`) you can access ZITADEL APIs
|
||||
* With a custom scope (`urn:zitadel:iam:org:project:id:zitadel:aud`) you can access ZITADEL APIs
|
||||
|
||||
Where to go from here:
|
||||
|
||||
|
@ -263,7 +263,7 @@ func (c *Commands) addUserToken(ctx context.Context, userWriteModel *UserWriteMo
|
||||
return nil, nil, caos_errs.ThrowNotFound(nil, "COMMAND-1d6Gg", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
audience = domain.AddAudScopeToAudience(audience, scopes)
|
||||
audience = domain.AddAudScopeToAudience(ctx, audience, scopes)
|
||||
|
||||
preferredLanguage := ""
|
||||
existingHuman, err := c.getHumanWriteModelByID(ctx, userWriteModel.AggregateID, userWriteModel.ResourceOwner)
|
||||
|
@ -151,19 +151,6 @@ func (a *AuthRequest) AppendAudIfNotExisting(aud string) {
|
||||
a.Audience = append(a.Audience, aud)
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopeProjectIDsForAud() []string {
|
||||
projectIDs := make([]string, 0)
|
||||
switch request := a.Request.(type) {
|
||||
case *AuthRequestOIDC:
|
||||
for _, scope := range request.Scopes {
|
||||
if strings.HasPrefix(scope, ProjectIDScope) && strings.HasSuffix(scope, AudSuffix) {
|
||||
projectIDs = append(projectIDs, strings.TrimSuffix(strings.TrimPrefix(scope, ProjectIDScope), AudSuffix))
|
||||
}
|
||||
}
|
||||
}
|
||||
return projectIDs
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopeOrgPrimaryDomain() string {
|
||||
switch request := a.Request.(type) {
|
||||
case *AuthRequestOIDC:
|
||||
|
@ -4,6 +4,7 @@ const (
|
||||
OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:"
|
||||
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
|
||||
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
|
||||
ProjectIDScopeZITADEL = "zitadel"
|
||||
AudSuffix = ":aud"
|
||||
SelectIDPScope = "urn:zitadel:iam:org:idp:id:"
|
||||
)
|
||||
|
@ -1,9 +1,11 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
@ -20,11 +22,16 @@ type Token struct {
|
||||
PreferredLanguage string
|
||||
}
|
||||
|
||||
func AddAudScopeToAudience(audience, scopes []string) []string {
|
||||
func AddAudScopeToAudience(ctx context.Context, audience, scopes []string) []string {
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, ProjectIDScope) && strings.HasSuffix(scope, AudSuffix) {
|
||||
audience = append(audience, strings.TrimSuffix(strings.TrimPrefix(scope, ProjectIDScope), AudSuffix))
|
||||
if !(strings.HasPrefix(scope, ProjectIDScope) && strings.HasSuffix(scope, AudSuffix)) {
|
||||
continue
|
||||
}
|
||||
projectID := strings.TrimSuffix(strings.TrimPrefix(scope, ProjectIDScope), AudSuffix)
|
||||
if projectID == ProjectIDScopeZITADEL {
|
||||
projectID = authz.GetInstance(ctx).ProjectID()
|
||||
}
|
||||
audience = append(audience, projectID)
|
||||
}
|
||||
return audience
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user