plain-megabyte-75926
03/18/2025, 3:41 PMroles
to a JWT token that is issued from Ory Hydra?brief-egg-61061
03/18/2025, 3:42 PMbrief-egg-61061
03/18/2025, 3:42 PMroles
to JWT tokens issued by Ory Hydra. Here are the main approaches: ### 1. Using a Custom Consent UI When implementing a custom consent UI, you can add custom claims to access tokens and ID tokens during consent acceptance: javascript import { Configuration, OAuth2Api } from "@ory/client" const ory = new OAuth2Api( new Configuration({ basePath: `https://${process.env.ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`, accessToken: process.env.ORY_API_KEY, }), ) export async function acceptConsent(consentChallenge: string) { const { data } = await ory.getOAuth2ConsentRequest({ consentChallenge }) return await ory .acceptOAuth2ConsentRequest({ consentChallenge: consentChallenge, acceptOAuth2ConsentRequest: { session: { access_token: { roles: ["admin", "user"], }, id_token: { roles: ["admin", "user"], }, }, }, }) .then(({ data }) => data) }
By default, custom claims will be nested under the ext
claim in the access token. If you want your roles
claim to be at the top level, you need to modify the configuration: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/allowed_top_level_claims=[\"roles\"]"
[Opaque and JWT access tokens](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token#custom-consent-ui-and-custom-claims-for-jwt-access-token) ### 2. Using OAuth2 Webhooks You can also use OAuth2 webhooks to customize claims in tokens: 1. First, register your webhook endpoint: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --add '/oauth2/token_hook="<https://my-example.app/token-hook>"' \ --format yaml
2. Then, in your webhook handler, respond with the custom claims: json { "session": { "access_token": { "roles": ["admin", "user"] }, "id_token": { "roles": ["admin", "user"] } } }
[Customizing claims with OAuth2 webhooks](https://www.ory.sh/docs/hydra/guides/claims-at-refresh) ### 3. For OIDC ID Tokens If you're specifically looking to add claims to ID tokens in an OpenID Connect flow, you can also customize the claims by: - Using identity traits if the claim should be derived from user data - Implementing a custom consent endpoint that adds the roles claim ### Important Notes - Required JWT claims can't be overwritten by custom claims - By default, all custom claims will be mirrored in the ext
claim, but you can disable this with: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/mirror_top_level_claims=false"
- For group memberships specifically, there's an open feature request to automatically include group memberships from Ory Keto in tokens, but currently this requires a custom implementation as described above Choose the approach that best fits your architecture and requirements.plain-megabyte-75926
03/18/2025, 3:43 PMbrief-egg-61061
03/18/2025, 3:43 PMbrief-egg-61061
03/18/2025, 3:44 PMory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --add '/oauth2/token_hook="<https://your-api.example/token-hook>"' \ --format yaml
### 2. Implement the Webhook Handler Your webhook handler should: 1. Extract the user ID from the webhook payload 2. Look up the user's roles in your database/system 3. Return those roles as claims in the response Here's a conceptual example of how your webhook handler might work: javascript <http://app.post|app.post>('/token-hook', async (req, res) => { // Extract user ID from the webhook payload const userId = req.body.session?.id_token?.subject; if (!userId) { // Return 204 to continue without modifications if no user ID return res.status(204).end(); } try { // Look up user roles in your database const userRoles = await getUserRolesFromDatabase(userId); // Return the roles as claims return res.status(200).json({ session: { access_token: { roles: userRoles }, id_token: { roles: userRoles } } }); } catch (error) { console.error('Error fetching user roles:', error); // Return 204 to continue without modifications if there's an error return res.status(204).end(); } });
### 3. Make the Roles Available at the Top Level (Optional) If you want the roles to be available at the top level of the token (not just under the ext
claim), configure the allowed top-level claims: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/allowed_top_level_claims=[\"roles\"]"
## Webhook Payload Structure The webhook payload will contain information about the user and the OAuth2 client. Here's a simplified example: json { "session": { "id_token": { "id_token_claims": { "iss": "<http://your-slug-xyz.projects.oryapis.com>", "sub": "user-id-here", "aud": ["app-client"] }, "subject": "user-id-here" }, "client_id": "app-client" }, "request": { "client_id": "app-client", "granted_scopes": ["offline", "openid"] } }
You can use the sub
claim or subject
field to identify the user and look up their roles. [Customizing claims with OAuth2 webhooks](https://www.ory.sh/docs/hydra/guides/claims-at-refresh) This approach allows you to dynamically assign roles to each user based on your own business logic and data storage, rather than having to hardcode roles in your OAuth2 configuration. Remember that the webhook must respond quickly to avoid timeouts in the authentication flow.plain-megabyte-75926
03/18/2025, 3:47 PMscp
(= sope) field instead of a custom claim within the JWT?brief-egg-61061
03/18/2025, 3:47 PMbrief-egg-61061
03/18/2025, 3:47 PMscp
(Scope) vs. Custom Claims for Roles ### Using OAuth2 Scopes for Roles Using the scp
field (OAuth2 scopes) for role information has some advantages: 1. Standard OAuth2 Approach: Scopes are a standard OAuth2 concept designed to represent permissions that a client is authorized to access on behalf of a user. 2. Built-in Support: Ory has built-in support for scope validation in components like Oathkeeper, which can check scopes in the JWT token. 3. Scope Format Options: Ory allows you to customize how scopes appear in tokens: ory patch oauth2-config {project.id} \ --replace '/strategies/jwt/scope_claim="string"'
This lets you choose between array format (scope: ["admin", "user"]
), string format (scp: "admin user"
), or both formats in your tokens. [Customize OpenID Connect scope and claims](https://www.ory.sh/docs/oauth2-oidc/openid-connect-claims-scope-custom) ### Important Distinction: Scopes vs. Permissions However, there's an important conceptual difference to consider: > "What's the difference between scopes and permissions? Scopes define what the client is authorized to access in the name of the user. Permissions (RBAC, ACL) define what the user themselves are allowed to do." [OAuth2 scopes represent user consent](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#oauth2-scopes-represent-user-consent) This means: - Scopes are about what a client application can do on behalf of a user - Roles/permissions are about what the user themselves can do ### Practical Considerations If you're using Ory Oathkeeper for API gateway protection, it has built-in support for scope validation in JWT tokens. It checks various scope claim formats (scp
, scope
, scopes
) and normalizes them: > "JSON Web Tokens can be scoped. However, that feature isn't standardized and several claims that represent the token scope have been seen in the wild: scp
, scope
, scopes
." [Oathkeeper JWT Scope](https://www.ory.sh/docs/oathkeeper/pipeline/authn#jwt-scope) ### Recommendation The best approach depends on your specific use case: 1. Use scopes (scp
) if you're primarily controlling what client applications can do on behalf of users, especially if you're using Oathkeeper for API protection. 2. Use custom claims (like roles
) if you're representing user-specific permissions that are inherent to the user regardless of which client is making the request. 3. Use both in some cases - scopes for client permissions and custom claims for user roles, especially in complex systems. If you decide to use scopes, you can set them during the consent flow or via the OAuth2 webhook just like you would with custom claims. Remember that with Ory, you can customize the scope claim format to match your preferred standard using the configuration option mentioned above.plain-megabyte-75926
03/18/2025, 3:55 PMbrief-egg-61061
03/18/2025, 3:55 PMbrief-egg-61061
03/18/2025, 3:55 PMscp
field in tokens is a widely recognized convention for API authorization. 2. Conceptual Alignment: As mentioned in the Ory documentation: > "Scopes define what the client is authorized to access in the name of the user. Permissions (RBAC, ACL) define what the user themselves are allowed to do." [OAuth2 scopes represent user consent](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#oauth2-scopes-represent-user-consent) This aligns well with API Gateway's purpose of controlling access to API resources. 3. Scope Validation Support: Ory has built-in support for different scope validation strategies that can be useful when integrating with API Gateway: > "Scope foo
matches foo
, foo.bar
, foo.baz
but not `bar`" [Oathkeeper API access rules](https://www.ory.sh/docs/oathkeeper/api-access-rules#scoped-credentials) When implementing this approach, you would: 1. Define your API permissions as scopes in Ory (e.g., api:read
, api:write
) 2. Issue tokens with these scopes during the OAuth2 flow 3. Configure your AWS API Gateway custom authorizer to validate these scopes against the required permissions for each endpoint This creates a clean separation of concerns where Ory handles authentication and scope issuance, while AWS API Gateway handles the authorization decision based on those scopes. If you have more complex authorization requirements that go beyond what scopes can represent, you might consider using Ory Keto alongside this approach for fine-grained permissions.plain-megabyte-75926
03/19/2025, 7:34 AMbrief-egg-61061
03/19/2025, 7:34 AMbrief-egg-61061
03/19/2025, 7:35 AMscp
field - For API-level permissions that clients are authorized to use 2. Roles in a custom claim - For user-specific role information You can set this up using the OAuth2 webhook approach: javascript // Example webhook response { "session": { "access_token": { "roles": ["admin", "finance"] // Custom claim for roles } } }
And ensure the scopes are included during the OAuth2 flow (either via consent or the webhook). ### 2. Configure Your API Gateway Authorizer In your AWS Lambda custom authorizer, implement logic that checks both: 1. Scopes - To verify what operations the client can perform 2. Roles - To verify what resources the user can access ### 3. Best Practices When implementing this approach, follow these best practices from Ory: 1. Don't cache permission checks - Ory Network provides low-latency responses with appropriate caching: > "Ory Network provides a low-latency, globally distributed system with appropriate caching, cache invalidation, and other optimizations. You don't need to worry about caching the permission checks in your application." [Best practices](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#best-practices) 2. Use fine-grained permissions - Whether using scopes or roles: > "Ory Permissions allows you to define fine-grained permissions, which can help you enforce the principle of least privilege." [Best practices](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#best-practices) 3. Perform permission checks in every critical path - Ensure your API Gateway authorizer is configured for all protected endpoints. ### Example Authorization Logic Your Lambda authorizer might implement logic like: IF request.method == "GET" AND "api:read" IN token.scp AND (resource.owner == token.sub OR "admin" IN token.roles) THEN allow ELSE deny
This combines: - Scope-based authorization (can this client perform read operations?) - Role-based authorization (does this user have admin privileges?) - Resource-based authorization (is this user the owner of the resource?) This hybrid approach gives you the flexibility to implement complex authorization patterns while still following OAuth2 best practices. If you need even more sophisticated permission models, you might consider using Ory Keto alongside this approach, as it's specifically designed for fine-grained access control based on the Google Zanzibar model.plain-megabyte-75926
03/19/2025, 7:43 AMscope
field within the JWT?brief-egg-61061
03/19/2025, 7:43 AMbrief-egg-61061
03/19/2025, 7:44 AMscope
field in JWTs for roles has both advantages and limitations when working with Ory. ### Using Scopes for Roles: Considerations While you can technically use scopes to represent roles, there's an important conceptual distinction in OAuth2: > "What's the difference between scopes and permissions? Scopes define what the client is authorized to access in the name of the user. Permissions (RBAC, ACL) define what the user themselves are allowed to do." [OAuth2 scopes represent user consent](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#oauth2-scopes-represent-user-consent) This means: - Scopes are designed to represent what a client application can do on behalf of a user - Roles typically represent what the user themselves can do ### Practical Implications If you decide to use scopes for roles, be aware that: 1. Scope Format Options: Ory allows you to customize how scopes appear in tokens: ory patch oauth2-config {project.id} \ --replace '/strategies/jwt/scope_claim="string"'
This lets you choose between array format (scope: ["admin", "user"]
), string format (scp: "admin user"
), or both formats. [Customize OpenID Connect scope and claims](https://www.ory.sh/docs/oauth2-oidc/openid-connect-claims-scope-custom) 2. Scope Validation Strategies: Ory supports different scope validation strategies: - `hierarchic`: Scope foo
matches foo
, foo.bar
, foo.baz
but not bar
- `wildcard`: Scope foo.*
matches foo
, foo.bar
, foo.baz
but not bar
- `exact`: Scope foo
matches foo
but not bar
nor foo.bar
- `none`: Scope validation is disabled [Oathkeeper API access rules](https://www.ory.sh/docs/oathkeeper/api-access-rules#scoped-credentials) 3. Scope Claim Formats: Ory checks various scope claim formats: > "JSON Web Tokens can be scoped. However, that feature isn't standardized and several claims that represent the token scope have been seen in the wild: scp
, scope
, scopes
." [Oathkeeper JWT Scope](https://www.ory.sh/docs/oathkeeper/pipeline/authn#jwt-scope) ### Recommendation For AWS API Gateway specifically (as we discussed previously), using scopes for roles can work well if: 1. Your roles directly map to API permissions (e.g., read-only
, admin-api
) 2. You're primarily controlling what client applications can do 3. You want to leverage the built-in scope validation in tools like Oathkeeper However, for more complex authorization models where roles represent organizational positions or user types rather than just permissions, using custom claims for roles alongside scopes for API permissions would provide a clearer separation of concerns. The best approach depends on your specific authorization requirements and how you plan to use these tokens across your system. If you do decide to use scopes for roles, make sure to document this decision clearly for your team to avoid confusion about the conceptual difference between scopes and roles in OAuth2.plain-megabyte-75926
03/19/2025, 3:16 PMscope
parameter in the HTTP request to the oauth2/token
?brief-egg-61061
03/19/2025, 3:16 PMbrief-egg-61061
03/19/2025, 3:17 PMscope
parameter depends on the grant type you're using and how Ory is configured. For the client credentials grant type, Ory has a specific configuration option that controls this behavior: ory patch oauth2-config "{your-project-id}" \ --replace '/oauth2/client_credentials/default_grant_allowed_scope=true'
When this setting is enabled, Ory will grant all the OAuth2 Client's allowed scopes when an empty scope is requested for a client_credentials
grant. This behavior mimics how MITREid works, as mentioned in the [Migrating from MITREid](https://www.ory.sh/docs/hydra/self-hosted/migrating-from-mitreid) documentation. By default, however, Ory Hydra returns an empty scope when no scope is specified in the request. For other grant types like the authorization code flow, the scopes that were approved during the consent phase will be included in the access token. If you're using a custom consent UI, you can control which scopes are granted during that process. When using JWT bearer grants, the scope
parameter is optional in the token request, as noted in the [JWT profile documentation](https://www.ory.sh/docs/hydra/guides/jwt#exchanging-jwt-assertion-for-access-token). It's worth noting that the OAuth2 standard generally expects clients to explicitly request the scopes they need, so explicitly specifying the scopes you require is considered a best practice for clarity and security. If you need specific scopes to be automatically included in all tokens, configuring the client credentials default grant behavior is likely the most appropriate approach.plain-megabyte-75926
03/19/2025, 9:55 PMbrief-egg-61061
03/19/2025, 9:55 PMbrief-egg-61061
03/19/2025, 9:55 PMdocument:read
, user:write
) provide more precise control over permissions. This approach: - Allows for more specific access control - Follows the principle of least privilege - Provides better auditability of permissions ### Coarse-grained Scopes (Role-like) Coarse-grained scopes (like admin
, editor
) function more like roles and: - Reduce the number of scopes to manage - Simplify client applications' scope requests - May be easier for users to understand during consent ## Ory's Perspective on Scopes Ory makes an important distinction between scopes and permissions: > "What's the difference between scopes and permissions? Scopes define what the client is authorized to access in the name of the user. Permissions (RBAC, ACL) define what the user themselves are allowed to do." [OAuth2 scopes represent user consent](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#oauth2-scopes-represent-user-consent) This distinction suggests that scopes are primarily about client application permissions, not user roles. ## Scope Validation Strategies in Ory Ory supports different scope validation strategies that can help with hierarchical or pattern-based scope designs: - `hierarchic`: Scope foo
matches foo
, foo.bar
, foo.baz
but not bar
- `wildcard`: Scope foo.*
matches foo
, foo.bar
, foo.baz
but not bar
- `exact`: Scope foo
matches foo
but not bar
nor foo.bar
[Oathkeeper API access rules](https://www.ory.sh/docs/oathkeeper/api-access-rules#scoped-credentials) These strategies can help you implement more sophisticated scope models. ## Best Practices Based on Ory's documentation and OAuth2 best practices: 1. Use fine-grained scopes for client permissions to follow the principle of least privilege: > "Ory Permissions allows you to define fine-grained permissions, which can help you enforce the principle of least privilege." [Best practices](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#best-practices) 2. Consider using Ory Permissions (Keto) for complex authorization models beyond what scopes can represent: > "Ory Permissions is a modern permission system you can use to create a complete authorization system for your application or website, no matter the size or the complexity of the ACLs (access-control lists) your use case requires." [Introduction to Ory Permissions](https://www.ory.sh/docs/keto) 3. Combine approaches if needed - Use scopes for client permissions and Ory Permissions for user-specific roles and permissions. ## Recommendation For most Ory implementations, a balanced approach works best: - Define moderately fine-grained scopes that represent distinct permissions (e.g., users:read
, users:write
) - Group related fine-grained scopes when appropriate (e.g., documents:*
) - Use Ory Permissions (Keto) for complex role-based or relationship-based authorization needs This approach maintains the conceptual integrity of OAuth2 scopes while providing sufficient granularity for access control. For complex systems, consider implementing RBAC using Ory Permissions as described in their [RBAC guide](https://www.ory.sh/docs/keto/guides/rbac) rather than trying to force roles into the scope model.plain-megabyte-75926
03/20/2025, 7:30 AM400 Bad Request
will be returned. Instead we would like that we get an access token, but without the invalid scope.brief-egg-61061
03/20/2025, 7:30 AMbrief-egg-61061
03/20/2025, 7:31 AM{
"error": {
"code": 400,
"status": "Bad Request",
"request": "38308051-dd5e-9f91-b053-5b32f751b281",
"reason": "The page token is invalid, do not craft your own page tokens",
"message": "The request was malformed or contained invalid parameters"
}
}
[Handling pagination errors](https://www.ory.sh/docs/guides/api-rest-pagination#handling-pagination-errors) Similarly, the Hydra API reference indicates that 400 Bad Request responses are returned for invalid parameters: 400 | Bad Request | Bad Request Error Response
[Hydra API Reference](https://www.ory.sh/docs/hydra/reference/api) There is no indication in the provided knowledge sources that Ory supports automatically filtering out invalid scopes and proceeding with only the valid ones. The OAuth2 standard generally expects clients to explicitly request valid scopes, and Ory appears to follow this standard by rejecting requests with invalid scopes rather than silently removing them. If you want to implement this behavior (getting an access token without the invalid scope rather than receiving an error), you would likely need to: 1. Create a custom middleware or proxy in front of Ory that validates scopes before passing the request 2. Implement custom logic in your application to handle scope validation and filtering 3. Consider using the OAuth2 webhook functionality to modify the token response, though this would still require the initial request to be valid For a more definitive answer or to request this feature, you might want to consider opening a feature request in the Ory GitHub repository.plain-megabyte-75926
03/21/2025, 10:08 AMbrief-egg-61061
03/21/2025, 10:08 AMbrief-egg-61061
03/21/2025, 10:08 AMory introspect token --project $project_id $access_token
This will return token metadata including whether the token is active, the client ID, scopes, and expiry time. [Run your own OAuth2 Server](https://www.ory.sh/blog/run-oauth2-server-open-source-api-security) ### 2. Verify Token Activity Always check that the token is active: json { "active": true, "client_id": "a9dff982-bbf0-44d4-9c96-a9ed54fa9bee", "exp": 1666953894, "iat": 1666950294, "iss": "<https://elastic-goldstine-1n7d31aph0.projects.oryapis.com>", "nbf": 1666950294, "sub": "a9dff982-bbf0-44d4-9c96-a9ed54fa9bee", "token_type": "Bearer", "token_use": "access_token" }
An inactive token should immediately result in a 401 Unauthorized response. ### 3. Verify Token Expiration Check that the token hasn't expired by examining the exp
claim in the introspection response. ### 4. Validate Scopes Verify that the token contains the required scopes for the requested operation: IF required_scope IN token.scope THEN allow ELSE deny
Remember that in the client credentials flow, scopes represent what the client application is authorized to do. [OAuth2 fundamentals](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#getting-a-client-token) ### 5. Verify Client ID Since in the client credentials flow, the sub
(subject) claim typically contains the client ID, verify that the client is authorized to access the requested resource. ### 6. Check Token Type Ensure the token is an access token by verifying the token_use
claim is set to "access_token". ### 7. Validate Issuer Verify the iss
(issuer) claim matches your expected Ory issuer URL. ## Common Issues to Watch For Be aware of these potential issues when implementing token validation: 1. Replay of client credentials grant: Ensure your system can detect and prevent token replay attacks. 2. Wrong or misconfigured OAuth 2.0 client: If you're seeing unexpected authorization failures, verify the client configuration. [Solutions for common OAuth2-related problems](https://www.ory.sh/docs/hydra/debug#refresh-token-flow-fails) ## Implementation Example Here's a conceptual example of how you might implement these checks: javascript async function validateToken(accessToken) { // Perform token introspection const introspection = await introspectToken(accessToken); // Check if token is active if (!introspection.active) { return { valid: false, reason: "Token is inactive" }; } // Check expiration if (introspection.exp < Math.floor(Date.now() / 1000)) { return { valid: false, reason: "Token has expired" }; } // Check issuer if (introspection.iss !== "<https://your-project.projects.oryapis.com>") { return { valid: false, reason: "Invalid token issuer" }; } // Check token type if (introspection.token_use !== "access_token") { return { valid: false, reason: "Not an access token" }; } return { valid: true, introspection }; }
By implementing these checks, you'll ensure that your resource server properly validates access tokens issued through the client credentials flow, maintaining the security of your API resources.plain-megabyte-75926
03/21/2025, 10:12 AMbrief-egg-61061
03/21/2025, 10:12 AMbrief-egg-61061
03/21/2025, 10:13 AMIF required_scope IN token.scope THEN allow ELSE deny
These scopes represent the permissions granted to the client application. 2. Client ID verification: Since the sub
claim typically contains the client ID in this flow, you can use this to apply client-specific permissions: IF token.sub == "allowed-client-id" THEN allow ELSE deny
3. Additional permission checks: You can and should implement additional permission checks based on your security requirements: - Resource ownership verification - Rate limiting for specific clients - Time-based access restrictions - IP-based restrictions As noted in the Ory documentation: > "Scopes define what the client is authorized to access in the name of the user. Permissions (RBAC, ACL) define what the user themselves are allowed to do." [OAuth2 scopes represent user consent](https://www.ory.sh/docs/oauth2-oidc/overview/oauth2-concepts#oauth2-scopes-represent-user-consent) In the Client Credentials flow, there is no user context, so the scopes directly represent the client's permissions. However, this doesn't mean you should skip permission checks - it means your permission model should be adapted to work with client identities rather than user identities. For more complex authorization needs, you might consider using Ory Keto alongside the OAuth2 flow to implement fine-grained permission checks based on the client identity.plain-megabyte-75926
03/24/2025, 8:28 AMbrief-egg-61061
03/24/2025, 8:28 AMbrief-egg-61061
03/24/2025, 8:28 AM/services/oauth2/config
key. ### Using the CLI (which uses the REST API) The most documented way to change the scope strategy is using the Ory CLI, which ultimately uses the REST API: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/strategies/jwt/scope_claim=\"string\""
This example changes the scope claim format, but you would use a similar approach to change the scope strategy. [Customize OpenID Connect scope and claims](https://www.ory.sh/docs/oauth2-oidc/openid-connect-claims-scope-custom) ### Available Scope Strategies Ory supports several scope validation strategies: - `hierarchic`: Scope foo
matches foo
, foo.bar
, foo.baz
but not bar
- `wildcard`: Scope foo.*
matches foo
, foo.bar
, foo.baz
but not bar
- `exact`: Scope foo
matches foo
but not bar
nor foo.bar
- `none`: Scope validation is disabled [Oathkeeper API access rules](https://www.ory.sh/docs/oathkeeper/api-access-rules#scoped-credentials) ### Important Note on Synchronization When using Ory Oathkeeper with Ory Hydra, it's important to keep the scope strategy synchronized between both services: > "When using Ory Oathkeeper together with Ory Hydra the scope strategy needs to be kept in sync." [Connect to Ory Hydra OAuth2 Token introspection](https://www.ory.sh/docs/oathkeeper/oauth2-token-introspection-hydra) This ensures consistent behavior across your Ory infrastructure when validating scopes.plain-megabyte-75926
05/28/2025, 6:47 AMbrief-egg-61061
05/28/2025, 6:47 AMbrief-egg-61061
05/28/2025, 6:48 AMplain-megabyte-75926
05/28/2025, 6:49 AMbrief-egg-61061
05/28/2025, 6:49 AMbrief-egg-61061
05/28/2025, 6:49 AMplain-megabyte-75926
05/28/2025, 7:00 AMbrief-egg-61061
05/28/2025, 7:00 AMbrief-egg-61061
05/28/2025, 7:00 AMsession
object when accepting the consent request. By default, these claims will appear under the ext
claim, but you can configure them to appear at the top level if needed. [Custom consent UI and custom claims for (JWT) access token](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token#custom-consent-ui-and-custom-claims-for-jwt-access-token) 2. Session to JWT Conversion (Ory Identities): If you are converting Ory sessions to JWTs (for example, for CORS or third-party integrations), you can use a JsonNet template to customize the claims in the JWT. This approach allows you to map session data or identity traits directly into JWT claims. [Convert Ory Sessions to JSON Web Tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors#json-web-token-templates) If you are using Ory Hydra/Ory Network for OAuth2 flows, the two main approaches for adding custom claims are the OAuth2 Webhook and the custom consent UI. For session-based JWTs, the tokenizer template approach is available. If you need more advanced or dynamic claim customization, the OAuth2 Webhook remains the most flexible option. If you need further details on a specific method, let me know!plain-megabyte-75926
05/28/2025, 7:05 AMbrief-egg-61061
05/28/2025, 7:05 AMbrief-egg-61061
05/28/2025, 7:06 AMplain-megabyte-75926
05/28/2025, 7:16 AMbrief-egg-61061
05/28/2025, 7:16 AMbrief-egg-61061
05/28/2025, 7:16 AMmetadata
field is intended for storing arbitrary JSON data of your choosing that is associated with the client. This can be used for internal tracking, annotations, or any custom information you want to attach to the client definition. The metadata is not used by Ory for any functional purpose—it is simply stored and returned as part of the client object. According to the documentation, the metadata
field is described as: > Metadata is an arbitrary JSON String of your choosing. (default "{}") [hydra create client options](https://www.ory.sh/docs/hydra/cli/hydra-create-client#options) There is no indication in the knowledge sources that the contents of the metadata
field are included in the JWT access or ID tokens issued by Ory. The metadata is part of the client configuration and is not automatically mapped to token claims. In summary: - The metadata
field is for storing custom, client-specific information. - This information is not included in the JWTs issued by Ory by default.plain-megabyte-75926
05/28/2025, 12:26 PMbrief-egg-61061
05/28/2025, 12:26 PMbrief-egg-61061
05/28/2025, 12:27 PMview
, edit
, delete
) for resources, rather than just checking for broad roles. Permissions are more flexible and allow you to enforce the principle of least privilege, ensuring users (or clients) have access only to what they need. Roles can be used to group permissions, but the actual access check at the endpoint should be for the required permission: > When you perform an action on behalf of a user, you should check for a specific permission, such as view
or edit
, instead of a relationship, such as owner
. The concrete permission is then still checked against the relationships. For example, if owners
can view
a file and the view
permission is checked, then the owners
relation is looked up in the relationships database. [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model#list-permissions-to-check-for-each-object) Additionally, Ory recommends: > Use fine-grained permissions... which can help you enforce the principle of least privilege. [Best practices](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#best-practices) Summary: Protect endpoints by checking for the required permission (e.g., edit:document
), not just by role. Roles can be mapped to permissions, but the permission check is what should be enforced at the API/resource level.plain-megabyte-75926
06/03/2025, 7:52 AMadmin/oauth2/introspect
?brief-egg-61061
06/03/2025, 7:52 AMbrief-egg-61061
06/03/2025, 7:53 AM/admin/oauth2/introspect
REST endpoint. The /admin/oauth2/introspect
endpoint allows you to check if an OAuth2 access or refresh token is active and to retrieve additional information about the token. This is the standard OAuth2 token introspection mechanism, and it can be used by any resource server—including one behind AWS API Gateway—to validate tokens received from clients. When a request comes to your API Gateway, you can configure a Lambda authorizer (or a similar mechanism) to call the /admin/oauth2/introspect
endpoint with the token. If the introspection response indicates the token is active ("active": true
), you can allow access; otherwise, you deny it. The introspection response also includes information such as scopes, client ID, subject, and more, which you can use for further authorization decisions. Important security note: The /admin/oauth2/introspect
endpoint is considered an administrative endpoint and should not be exposed to the public internet. You should ensure that only trusted components (like your Lambda authorizer) can access this endpoint, as it requires authentication and has no built-in access control beyond that. The Ory documentation recommends protecting administrative endpoints with an API Gateway or Authorization Proxy if you need to expose them beyond your internal network [Exposing administrative and public API endpoints](https://www.ory.sh/docs/hydra/self-hosted/production#exposing-administrative-and-public-api-endpoints). Summary: - You can use AWS API Gateway and validate tokens by calling /admin/oauth2/introspect
directly, without Oathkeeper. - Ensure the introspection endpoint is properly secured and not exposed to untrusted clients. - This approach is supported and aligns with standard OAuth2 resource server patterns [OAuth 2.0 token introspection](https://www.ory.sh/docs/hydra/guides/oauth2-token-introspection).