In the release 2.0 of Ory Hydra was announced that they have now an integration with Kratos their identity provider. It is also mentioned that this is possible to achieve by doing some configuration "Ory Identities is now compatible with the Ory OAuth2 Login and Consent Flow. This means, for example, that Ory Kratos can be the login provider for Ory Hydra with a bit of configuration."
Have someone done this configuration, is there any example I can follow to use Kratos as identity provider for Hydra?
Release notes: https://github.com/ory/hydra/releases/tag/v2.0.0
So far I was able to setup a docker compose file where I have a Postgres database and I also have Hydra and Kratos. What I don't know is how to make them interact with each other.
@Hodgson's answer is a great start for Kratos v1.1.0 and Hydra v2.2.0. Unfortunately, the provided docker compose yaml and hydra config are using hydra-login-consent-node (port 3000) for the consent route -- which doesn't use Kratos identity traits for requested+granted scopes (e.g.: email, profile).
Kratos SSUI was recently updated to include its own consent route, and has also been updated to extract traits from Kratos for id_token, based on the requested+granted scope (email, profile). Review the code at: https://github.com/ory/kratos-selfservice-ui-node/blob/master/src/routes/consent.ts
Recommended changes to @Hodgson's answer:
Completely comment-out or remove the "consent" section from the docker compose file (quickstart.yml).
Change config/hydra.yml:
urls:
# consent: http://127.0.0.1:3000/consent
consent: http://127.0.0.1:4455/consent
To add First Name, Last Name (accessible via Kratos SSUI's consent route with "profile" scope) to your Kratos identity schema, see https://github.com/ory/kratos/blob/master/contrib/quickstart/kratos/email-password/identity.schema.json
The prior answer is also missing details on how to create an OIDC client:
docker ps
# Get ID of hydra container
docker exec -it HYDRA_CONTAINER_ID \
hydra create client \
-e http://hydra:4445 \
--token-endpoint-auth-method client_secret_basic \
--name "OIDC Client Name" \
--skip-consent=false \
--logo-uri https://www.example.com:8443/logo/image.png \
--policy-uri https://www.example.com:8443/privacy.html \
--tos-uri https://www.example.com:8443/terms.html \
--redirect-uri https://www.example.com:8443/auth/login/ory/callback \
--redirect-uri https://www.example.com:8443/app/index.html \
--redirect-uri http://127.0.0.1:5555/callback \
--redirect-uri http://127.0.0.1:4455 \
--grant-type "authorization_code,refresh_token" \
--response-type "code" \
--scope "openid,offline_access,email,profile" \
--format json-pretty
Set/include/exclude the URLs (logo, policy, tos, redirect/s) as needed.
To disable prompting the user to grant consent for the requested scopes, either set skip-consent=true when creating the OIDC client, or add an environment variable to the kratos section in quickstart.yml for TRUSTED_CLIENT_IDS, set to the OIDC Client IDs (comma-separated) that you want to auto-accept scope requests for (skipping the consent UI).
Quick tip: If you're using webauthn (Yubikey, Touch ID, etc) with Kratos, you'll need to change most of the references to localhost (instead of 127.0.0.1), and/or use a FQDN and SSL Certificate. Below is some quick code to get a short-lived cert from Let's Encrypt.
mkdir -p certbot/{etc,lib,log,opt}
docker run -it --rm \
--name=certbot --hostname=certbot \
-v ${PWD}/certbot/etc:/etc/letsencrypt \
-v ${PWD}/certbot/lib:/var/lib/letsencrypt \
-v ${PWD}/certbot/log:/var/log/letsencrypt \
certbot/certbot:v2.9.0 \
certonly \
--manual \
--preferred-challenges dns \
-d YOUR_DOMAIN_NAME \
--email YOUR_EMAIL_ADDRESS \
--agree-tos
At the DNS provider for YOUR_DOMAIN_NAME, add record (value provided by Certbot):
TXT _acme-challenge.SUB_DOMAIN CERTBOT_CHALLENGE
Manually verify the new TXT record, then proceed in Certbot
nslookup -q=txt _acme-challenge.SUB_DOMAIN.YOUR_TOPLEVEL_DOMAIN
Then you can add an entry to your hosts file (127.0.0.1 YOUR_DOMAIN_NAME) to ensure proper resolution locally, and use NGINX to proxy or host your web app, referencing the public (fullchain.pem) and private (privkey.pem) certs/keys in your certbot/etc/live/YOUR_DOMAIN_NAME folder. For access outside your network, add an A record with your external IP at the DNS provider for YOUR_DOMAIN_NAME, and add any necessary port forwarding rules in your ISP's router and/or your home router.
To get everything working in my setup, I also needed to change the Hydra urls.self.issuer to the FQDN (http://YOUR_DOMAIN_NAME:4444), and customize the docker compose config for the app using the OIDC Client so internal attempts to access the public Hydra URL (port 4444) would be re-routed through my host system, so redirects could be properly processed. This might not be needed if your app is running bare-metal or on a different docker network than your Ory services.
extra_hosts:
- "YOUR_DOMAIN_NAME:host-gateway"
Kratos and Hydra need to be on the same Docker network, and then oauth2_provider.url needs to be set to http://hydra:4445 in Kratos.
In 2024, this works with Hydra v2.2.0 and Kratos v1.1.0
# quickstart.yml
###########################################################################
####### FOR DEMONSTRATION PURPOSES ONLY #######
###########################################################################
version: "3.7"
services:
### KRATOS ###
kratos-migrate:
image: oryd/kratos:v1.1.0
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc
volumes:
- type: volume
source: kratos-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./config
target: /etc/config/kratos
command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes
restart: on-failure
networks:
- intranet
kratos-selfservice-ui-node:
image: oryd/kratos-selfservice-ui-node:v1.1.0
ports:
- "4455:4455"
environment:
- PORT=4455
- KRATOS_PUBLIC_URL=http://kratos:4433
- KRATOS_BROWSER_URL=http://127.0.0.1:4433
- COOKIE_SECRET=changeme
- CSRF_COOKIE_NAME=cookie_name
- CSRF_COOKIE_SECRET=changeme
- DANGEROUSLY_DISABLE_SECURE_CSRF_COOKIES=true
networks:
- intranet
restart: on-failure
kratos:
depends_on:
- kratos-migrate
image: oryd/kratos:v1.1.0
ports:
- '4433:4433' # public
- '4434:4434' # admin
restart: unless-stopped
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true
- LOG_LEVEL=trace
command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier
volumes:
- type: volume
source: kratos-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./config
target: /etc/config/kratos
networks:
- intranet
### HYDRA ###
hydra:
image: oryd/hydra:v2.2.0
ports:
- "4444:4444" # Public port
- "4445:4445" # Admin port
- "5555:5555" # Port for hydra token user
command: serve -c /etc/config/hydra/hydra.yml all --dev
volumes:
- type: volume
source: hydra-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./config
target: /etc/config/hydra
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true
restart: unless-stopped
depends_on:
- hydra-migrate
networks:
- intranet
hydra-migrate:
image: oryd/hydra:v2.2.0
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true
command: migrate -c /etc/config/hydra/hydra.yml sql -e --yes
volumes:
- type: volume
source: hydra-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./config
target: /etc/config/hydra
restart: on-failure
networks:
- intranet
consent:
environment:
- HYDRA_ADMIN_URL=http://hydra:4445
image: oryd/hydra-login-consent-node:v2.2.0
ports:
- "3000:3000"
restart: unless-stopped
networks:
- intranet
networks:
intranet:
volumes:
hydra-sqlite:
kratos-sqlite:
# config/kratos.yml
version: v1.1.0
dsn: memory
serve:
public:
base_url: http://127.0.0.1:4433
cors:
enabled: true
admin:
base_url: http://127.0.0.1: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
config:
min_password_length: 6
identifier_similarity_check_enabled: false
haveibeenpwned_enabled: false
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
logout:
after:
default_browser_return_url: http://127.0.0.1:4455/login
login:
ui_url: http://127.0.0.1:4455/login
registration:
ui_url: http://127.0.0.1:4455/registration
after:
password:
hooks:
- hook: session
log:
format: text
leak_sensitive_values: true
secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL
identity:
default_schema_id: default
schemas:
- id: default
url: file:///etc/config/kratos/identity.schema.json
courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true
oauth2_provider:
url: http://hydra:4445 # Integrate Kratos and Hydra
# config/hydra.yml
serve:
cookies:
same_site_mode: Lax
urls:
self:
issuer: http://127.0.0.1:4444
consent: http://127.0.0.1:3000/consent
login: http://127.0.0.1:4455/login
logout: http://127.0.0.1:4455/logout
identity_provider:
publicUrl: http://127.0.0.1:4433
url: http://127.0.0.1:4434
secrets:
system:
- youReallyNeedToChangeThis
oidc:
subject_identifiers:
supported_types:
- pairwise
- public
pairwise:
salt: youReallyNeedToChangeThis
config/identity.schema.json
{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
},
"required": [
"email"
],
"additionalProperties": false
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With