bumpy-controller-43157
01/22/2025, 9:37 AMbrief-egg-61061
01/22/2025, 9:37 AMbrief-egg-61061
01/22/2025, 9:37 AMkratosPublicUrl
and kratosBrowserUrl
are pointing to the same URL. As mentioned in the [GitHub discussion](https://github.com/ory/kratos/discussions/3695), one user resolved the issue by setting both to the same URL: kratosPublicUrl: <https://dev-server.local.net/kratos> kratosBrowserUrl: <https://dev-server.local.net/kratos>
2. Verify CORS settings: Ensure that your CORS configuration in Kratos allows the origin of your self-service UI. In the Helm configuration example provided, we can see a CORS setup like this: yaml cors: enabled: true allowed_origins: - <https://dev-server.local.net/kratos-ui/> - <http://dev-server.local.net/kratos-ui/>
Make sure your UI's origin is listed in the allowed origins. 3. Check cookie settings: If you're running over HTTP locally, make sure to use the --dev
flag when starting Kratos. This disables the Secure
flag on cookies, which is necessary for HTTP. As mentioned in the [CSRF troubleshooting docs](https://www.ory.sh/docs/kratos/debug/csrf): kratos serve --dev
However, remember not to use this in production. 4. Consistent domain usage: Ensure you're using either localhost
or 127.0.0.1
consistently across your configuration. Mixing these can cause issues as they're treated as separate domains. 5. Check for outdated SDK versions: If you're using an SDK, make sure it's up to date. As mentioned in the [GitHub issue comments](https://github.com/ory/network/issues/124), using an outdated SDK version can cause issues with redirects. If you're still experiencing issues after checking these points, you may need to review your entire configuration for any inconsistencies or seek further assistance from the Ory community.bumpy-controller-43157
01/22/2025, 9:38 AMbrief-egg-61061
01/22/2025, 9:38 AMbrief-egg-61061
01/22/2025, 9:39 AMyaml version: '3.7' services: kratos: image: oryd/kratos:v0.13.0 ports: - "4433:4433" # public - "4434:4434" # admin environment: - DSN=memory - LOG_LEVEL=debug - SERVE_PUBLIC_BASE_URL=<http://127.0.0.1:4433/> - SERVE_ADMIN_BASE_URL=<http://kratos:4434/> - SELFSERVICE_DEFAULT_BROWSER_RETURN_URL=<http://127.0.0.1:4455/> volumes: - type: bind source: ./kratos target: /etc/config/kratos command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier networks: - intranet kratos-selfservice-ui-node: image: oryd/kratos-selfservice-ui-node:latest ports: - "4455:3000" environment: - KRATOS_PUBLIC_URL=<http://kratos:4433/> - KRATOS_BROWSER_URL=<http://127.0.0.1:4433/> networks: - intranet depends_on: - kratos mailslurper: image: oryd/mailslurper:latest-smtps ports: - "4436:4436" - "4437:4437" networks: - intranet networks: intranet:
This Docker Compose file is based on the configuration provided in the [Ory Kratos Quickstart documentation](https://www.ory.sh/docs/kratos/quickstart#clone-ory-kratos-and-run-it-in-docker). Here's a breakdown of what this configuration does: 1. It sets up three services: kratos
, kratos-selfservice-ui-node
, and mailslurper
. 2. The kratos
service uses the official Ory Kratos image and exposes the public API on port 4433 and the admin API on port 4434. It's configured to use an in-memory database for simplicity. 3. The kratos-selfservice-ui-node
service uses the official Kratos Self-Service UI Node image and exposes the UI on port 4455. 4. The mailslurper
service is included to handle email sending for development purposes. 5. All services are connected to an internal Docker network called intranet
. To use this configuration: 1. Save the above YAML content in a file named docker-compose.yml
. 2. Create a kratos
directory in the same location as your docker-compose.yml
file. 3. In the kratos
directory, you'll need to add your Kratos configuration files, including kratos.yml
and identity.schema.json
. Then, you can start the services by running: docker-compose up
Remember to adjust the configuration as needed for your specific setup, especially the URLs and ports if you're not running on localhost. [Ory Kratos Quickstart](https://www.ory.sh/docs/kratos/quickstart#clone-ory-kratos-and-run-it-in-docker) provides more detailed information on setting up and configuring Kratos.bumpy-controller-43157
01/22/2025, 9:42 AMbrief-egg-61061
01/22/2025, 9:42 AMbrief-egg-61061
01/22/2025, 9:42 AMkratos.json
file:
json
{
"version": "v0.13.0",
"dsn": "memory",
"serve": {
"public": {
"base_url": "<http://127.0.0.1:4433/>",
"cors": {
"enabled": true
}
},
"admin": {
"base_url": "<http://kratos:4434/>"
}
},
"selfservice": {
"default_browser_return_url": "<http://127.0.0.1:4455/>",
"allowed_return_urls": [
"<http://127.0.0.1:4455>"
],
"methods": {
"password": {
"enabled": true
},
"totp": {
"config": {
"issuer": "Kratos"
},
"enabled": true
},
"lookup_secret": {
"enabled": true
},
"link": {
"enabled": true
},
"code": {
"enabled": true
}
},
"flows": {
"error": {
"ui_url": "<http://127.0.0.1:4455/error>"
},
"settings": {
"ui_url": "<http://127.0.0.1:4455/settings>",
"privileged_session_max_age": "15m",
"required_aal": "highest_available"
},
"recovery": {
"enabled": true,
"ui_url": "<http://127.0.0.1:4455/recovery>",
"use": "code"
},
"verification": {
"enabled": true,
"ui_url": "<http://127.0.0.1:4455/verification>",
"use": "code",
"after": {
"default_browser_return_url": "<http://127.0.0.1:4455/>"
}
},
"logout": {
"after": {
"default_browser_return_url": "<http://127.0.0.1:4455/login>"
}
},
"login": {
"ui_url": "<http://127.0.0.1:4455/login>",
"lifespan": "10m"
},
"registration": {
"lifespan": "10m",
"ui_url": "<http://127.0.0.1:4455/registration>",
"after": {
"password": {
"hooks": [
{
"hook": "session"
},
{
"hook": "show_verification_ui"
}
]
}
}
}
}
},
"log": {
"level": "debug",
"format": "text",
"leak_sensitive_values": true
},
"secrets": {
"cookie": [
"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE"
],
"cipher": [
"32-LONG-SECRET-NOT-SECURE-AT-ALL"
]
},
"ciphers": {
"algorithm": "xchacha20-poly1305"
},
"hashers": {
"algorithm": "bcrypt",
"bcrypt": {
"cost": 8
}
},
"identity": {
"default_schema_id": "default",
"schemas": [
{
"id": "default",
"url": "file:///etc/config/kratos/identity.schema.json"
}
]
}
}
To use this configuration:brief-egg-61061
01/22/2025, 9:42 AMkratos.json
file: json { "version": "v0.13.0", "dsn": "memory", "serve": { "public": { "base_url": "<http://127.0.0.1:4433/>", "cors": { "enabled": true } }, "admin": { "base_url": "<http://kratos:4434/>" } }, "selfservice": { "default_browser_return_url": "<http://127.0.0.1:4455/>", "allowed_return_urls": [ "<http://127.0.0.1:4455>" ], "methods": { "password": { "enabled": true }, "totp": { "config": { "issuer": "Kratos" }, "enabled": true }, "lookup_secret": { "enabled": true }, "link": { "enabled": true }, "code": { "enabled": true } }, "flows": { "error": { "ui_url": "<http://127.0.0.1:4455/error>" }, "settings": { "ui_url": "<http://127.0.0.1:4455/settings>", "privileged_session_max_age": "15m", "required_aal": "highest_available" }, "recovery": { "enabled": true, "ui_url": "<http://127.0.0.1:4455/recovery>", "use": "code" }, "verification": { "enabled": true, "ui_url": "<http://127.0.0.1:4455/verification>", "use": "code", "after": { "default_browser_return_url": "<http://127.0.0.1:4455/>" } }, "logout": { "after": { "default_browser_return_url": "<http://127.0.0.1:4455/login>" } }, "login": { "ui_url": "<http://127.0.0.1:4455/login>", "lifespan": "10m" }, "registration": { "lifespan": "10m", "ui_url": "<http://127.0.0.1:4455/registration>", "after": { "password": { "hooks": [ { "hook": "session" }, { "hook": "show_verification_ui" } ] } } } } }, "log": { "level": "debug", "format": "text", "leak_sensitive_values": true }, "secrets": { "cookie": [ "PLEASE-CHANGE-ME-I-AM-VERY-INSECURE" ], "cipher": [ "32-LONG-SECRET-NOT-SECURE-AT-ALL" ] }, "ciphers": { "algorithm": "xchacha20-poly1305" }, "hashers": { "algorithm": "bcrypt", "bcrypt": { "cost": 8 } }, "identity": { "default_schema_id": "default", "schemas": [ { "id": "default", "url": "file:///etc/config/kratos/identity.schema.json" } ] } }
To use this configuration: 1. Save this JSON content in a file named kratos.json
in the kratos
directory you created earlier (the one that's mounted as a volume in the Docker Compose file). 2. In your Docker Compose file, update the kratos
service command to use the JSON file: yaml command: serve -c /etc/config/kratos/kratos.json --dev --watch-courier
3. Make sure you also have an identity.schema.json
file in the same kratos
directory. This file defines the structure of your identity objects. This configuration sets up Kratos to use in-memory storage, enables various self-service flows (login, registration, recovery, etc.), and configures the URLs to match the Docker Compose setup. Remember to replace the placeholder secrets (PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
and 32-LONG-SECRET-NOT-SECURE-AT-ALL
) with your own secure secrets in a production environment. Also, note that this configuration is set up for a local development environment. For a production setup, you would need to make several changes, such as using a persistent database instead of in-memory storage, configuring proper secrets, and setting up HTTPS. [Ory Kratos Configuration Reference](https://www.ory.sh/docs/kratos/reference/configuration) provides more detailed information on all available configuration options.bumpy-controller-43157
01/23/2025, 7:55 AMory create oauth2-client
cli, how can i put an additional claim in the access tokens, it will have a static valuebrief-egg-61061
01/23/2025, 7:55 AMbrief-egg-61061
01/23/2025, 7:55 AMory create oauth2-client
CLI command, you'll need to follow a few steps. This process involves creating the client, then setting up a custom consent UI or using OAuth2 webhooks to add the custom claim. 1. First, create your OAuth2 client using the CLI command: ory create oauth2-client --project $project_id \ --name "Your Client Name" \ --grant-type authorization_code,client_credentials \ --response-type code \ --scope openid,offline
2. After creating the client, you have two main options to add a custom claim to the access token: a. Use a custom consent UI b. Set up an OAuth2 webhook For this example, let's use the OAuth2 webhook method as it's more straightforward for adding a static claim. 3. Set up an OAuth2 webhook: Use the Ory CLI to configure the webhook: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --add '/oauth2/token_hook="<https://your-webhook-endpoint.com/token-hook>"' \ --format yaml
Replace <https://your-webhook-endpoint.com/token-hook>
with the actual URL of your webhook endpoint. 4. Implement the webhook endpoint: Your webhook should respond with a JSON payload that includes the custom claim. Here's an example of how your webhook response should look: json { "session": { "access_token": { "your_custom_claim": "static_value" } } }
5. Configure allowed top-level claims: To add your custom claim at the top level of the access token, you need to configure the allowed top-level claims: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/allowed_top_level_claims=[\"your_custom_claim\"]"
This step allows your custom claim to be included at the top level of the access token. After setting up the webhook and configuring the allowed top-level claims, the access tokens issued for your OAuth2 client will include the custom claim with the static value you specified. The resulting access token structure will look something like this: json { "sub": "...", "your_custom_claim": "static_value", "ext": { "your_custom_claim": "static_value" } // ... other standard claims }
Remember that this setup requires you to have a webhook endpoint that can handle the token customization requests. If you don't want to set up a webhook, you would need to implement a custom consent UI, which is a more complex process. For more detailed information on customizing claims and using OAuth2 webhooks, you can refer to the [Ory documentation on customizing claims with OAuth2 webhooks](https://www.ory.sh/docs/hydra/guides/claims-at-refresh).bumpy-controller-43157
01/23/2025, 9:50 AMbrief-egg-61061
01/23/2025, 9:50 AMbrief-egg-61061
01/23/2025, 9:50 AMgo
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
ory "<http://github.com/ory/client-go|github.com/ory/client-go>"
)
type WebhookResponse struct {
Session struct {
AccessToken map[string]interface{} `json:"access_token"`
} `json:"session"`
}
func tokenHookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
var webhookPayload ory.TokenHookRequest
err := json.NewDecoder(r.Body).Decode(&webhookPayload)
if err != nil {
http.Error(w, "Invalid JSON input", http.StatusBadRequest)
return
}
// Log the received payload for debugging
log.Printf("Received webhook payload: %+v\n", webhookPayload)
// Prepare the response with the custom claim
response := WebhookResponse{}
response.Session.AccessToken = map[string]interface{}{
"custom_claim": "static_value",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func main() {
http.HandleFunc("/token-hook", tokenHookHandler)
fmt.Println("Starting webhook server on :8080...")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
This implementation does the following:
1. We import the necessary packages, including the Ory SDK (`github.com/ory/client-go`).
2. We define a WebhookResponse
struct that matches the expected response format for the OAuth2 webhook.
3. The tokenHookHandler
function handles the webhook requests:
- It checks if the request method is POST.
- It decodes the incoming JSON payload into an ory.TokenHookRequest
struct, which is provided by the Ory SDK.
- It logs the received payload for debugging purposes.
- It prepares a response with a custom claim (custom_claim
) set to a static value (static_value
).
- It sends the response back as JSON.
4. In the main
function, we set up an HTTP server that listens on port 8080 and registers the /token-hook
endpoint.
To use this webhook:
1. Make sure you have the Ory SDK installed:
go get <http://github.com/ory/client-go|github.com/ory/client-go>
2. Save the code in a file (e.g., webhook.go
).
3. Run the webhook server:
go run webhook.go
4. Configure Ory to use this webhook (assuming you're running it locally):
ory patch oauth2-config --project <project-id> --workspace <workspace-id> \
--add '/oauth2/token_hook="<http://localhost:8080/token-hook>"' \
--format yaml
5. Don't forget to configure the allowed top-level claims as mentioned in the previous answer:
ory patch oauth2-config --project <project-id> --workspace <workspace-id> \
--replace "/oauth2/allowed_top_level_claims=[\"custom_claim\"]"
brief-egg-61061
01/23/2025, 9:50 AMgo package main import ( "encoding/json" "fmt" "log" "net/http" ory "<http://github.com/ory/client-go|github.com/ory/client-go>" ) type WebhookResponse struct { Session struct { AccessToken map[string]interface{} `json:"access_token"` } `json:"session"` } func tokenHookHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) return } var webhookPayload ory.TokenHookRequest err := json.NewDecoder(r.Body).Decode(&webhookPayload) if err != nil { http.Error(w, "Invalid JSON input", http.StatusBadRequest) return } // Log the received payload for debugging log.Printf("Received webhook payload: %+v\n", webhookPayload) // Prepare the response with the custom claim response := WebhookResponse{} response.Session.AccessToken = map[string]interface{}{ "custom_claim": "static_value", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } func main() { http.HandleFunc("/token-hook", tokenHookHandler) fmt.Println("Starting webhook server on :8080...") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatalf("Server failed to start: %v", err) } }
This implementation does the following: 1. We import the necessary packages, including the Ory SDK (`github.com/ory/client-go`). 2. We define a WebhookResponse
struct that matches the expected response format for the OAuth2 webhook. 3. The tokenHookHandler
function handles the webhook requests: - It checks if the request method is POST. - It decodes the incoming JSON payload into an ory.TokenHookRequest
struct, which is provided by the Ory SDK. - It logs the received payload for debugging purposes. - It prepares a response with a custom claim (custom_claim
) set to a static value (static_value
). - It sends the response back as JSON. 4. In the main
function, we set up an HTTP server that listens on port 8080 and registers the /token-hook
endpoint. To use this webhook: 1. Make sure you have the Ory SDK installed: go get <http://github.com/ory/client-go|github.com/ory/client-go>
2. Save the code in a file (e.g., webhook.go
). 3. Run the webhook server: go run webhook.go
4. Configure Ory to use this webhook (assuming you're running it locally): ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --add '/oauth2/token_hook="<http://localhost:8080/token-hook>"' \ --format yaml
5. Don't forget to configure the allowed top-level claims as mentioned in the previous answer: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/allowed_top_level_claims=[\"custom_claim\"]"
This webhook will add a custom_claim
with the value static_value
to every access token issued. You can modify the tokenHookHandler
function to add more complex logic or dynamic values as needed. Remember to handle errors appropriately and add proper authentication to your webhook in a production environment. The [Ory documentation on customizing claims with OAuth2 webhooks](https://www.ory.sh/docs/hydra/guides/claims-at-refresh) provides more information on secure webhook implementation.bumpy-controller-43157
01/23/2025, 1:15 PMbrief-egg-61061
01/23/2025, 1:16 PMbrief-egg-61061
01/23/2025, 1:16 PMjavascript
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: {
some_custom_claim: "some_custom_value",
},
id_token: {
id_custom_claim: "some_value",
},
},
},
})
.then(({ data }) => data)
}
2. This will result in an access token with the following structure:
json
{
"sub": "...",
// ...
"ext": {
"some_custom_claim": "some_custom_value",
},
// ...
}
3. If you want the custom claim to be at the top level of the access token instead of nested under "ext", you need to modify the /oauth2/allowed_top_level_claims
configuration:
ory patch oauth2-config --project <project-id> --workspace <workspace-id> \
--replace "/oauth2/allowed_top_level_claims=[\"some_custom_claim\"]"
This will result in an access token with the following structure:
json
{
"sub": "...",
// ...
"some_custom_claim": "some_custom_value",
"ext": {
"some_custom_claim": "some_custom_value"
}
// ...
}
4. If you don't want to mirror the claims under the "ext" claim, you can disable this behavior:
ory patch oauth2-config --project <project-id> --workspace <workspace-id> \
--replace "/oauth2/mirror_top_level_claims=false"
This approach allows you to add custom claims without setting up a separate webhook. However, keep in mind that this method requires you to implement a custom consent flow or modify your existing consent acceptance logic.
[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)brief-egg-61061
01/23/2025, 1:16 PMjavascript 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: { some_custom_claim: "some_custom_value", }, id_token: { id_custom_claim: "some_value", }, }, }, }) .then(({ data }) => data) }
2. This will result in an access token with the following structure: json { "sub": "...", // ... "ext": { "some_custom_claim": "some_custom_value", }, // ... }
3. If you want the custom claim to be at the top level of the access token instead of nested under "ext", you need to modify the /oauth2/allowed_top_level_claims
configuration: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/allowed_top_level_claims=[\"some_custom_claim\"]"
This will result in an access token with the following structure: json { "sub": "...", // ... "some_custom_claim": "some_custom_value", "ext": { "some_custom_claim": "some_custom_value" } // ... }
4. If you don't want to mirror the claims under the "ext" claim, you can disable this behavior: ory patch oauth2-config --project <project-id> --workspace <workspace-id> \ --replace "/oauth2/mirror_top_level_claims=false"
This approach allows you to add custom claims without setting up a separate webhook. However, keep in mind that this method requires you to implement a custom consent flow or modify your existing consent acceptance logic. [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) Remember that this method gives you control over the claims at the time of consent, which might be suitable for dynamic values. For completely static values, the webhook method we discussed earlier might still be more convenient as it doesn't require modifying the consent flow.bumpy-controller-43157
01/23/2025, 1:17 PMbrief-egg-61061
01/23/2025, 1:17 PMbrief-egg-61061
01/23/2025, 1:17 PM