Skip to content

Commit f3f3a5d

Browse files
committed
feat: add support for exchanging session to JWT in Kratos
1 parent aa2f0d2 commit f3f3a5d

File tree

8 files changed

+144
-2
lines changed

8 files changed

+144
-2
lines changed

charts/management-portal/templates/secrets-keystore.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ metadata:
1010
type: Opaque
1111
data:
1212
keystore.p12: {{ .Values.keystore }}
13+
public-jwks.json: {{ .Values.public_jwks }}

charts/management-portal/values.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ keystore: ""
277277
# or with SOPS
278278
# keystore: {{ exec "sops" (list "-d" "keystore.p12") | b64enc | quote }}
279279

280+
# The public keys used for JWT signing in radar-kratos
281+
public_jwks: ""
282+
# With helmfile, this can be set in a production.yaml.gotmpl
283+
# file by setting
284+
# public_jwks: {{ readFile "../etc/management-portal/public-kratos-jwks.json" | b64enc | quote }}
285+
280286
# Configuration of the Postgres database to store data from Management Portal
281287
postgres:
282288
# -- host name of the postgres db

charts/radar-hydra/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Consult the [documentation](https://artifacthub.io/packages/helm/ory/hydra) of t
4949
| oauth_clients[0].grantTypes[1] | string | `"refresh_token"` | |
5050
| oauth_clients[0].responseTypes[0] | string | `"code"` | |
5151
| oauth_clients[0].responseTypes[1] | string | `"id_token"` | |
52-
| oauth_clients[0].scope | string | `"SOURCEDATA.CREATE SOURCETYPE.UPDATE SOURCETYPE.DELETE AUTHORITY.UPDATE MEASUREMENT.DELETE PROJECT.READ AUDIT.CREATE USER.DELETE AUTHORITY.DELETE SUBJECT.DELETE MEASUREMENT.UPDATE SOURCEDATA.UPDATE SUBJECT.READ USER.UPDATE SOURCETYPE.CREATE AUTHORITY.READ USER.CREATE SOURCE.CREATE SOURCE.READ SUBJECT.CREATE ROLE.UPDATE ROLE.READ MEASUREMENT.READ PROJECT.UPDATE PROJECT.DELETE ROLE.DELETE SOURCE.DELETE SOURCETYPE.READ ROLE.CREATE SOURCEDATA.DELETE SUBJECT.UPDATE SOURCE.UPDATE PROJECT.CREATE AUDIT.READ MEASUREMENT.CREATE AUDIT.DELETE AUDIT.UPDATE AUTHORITY.CREATE USER.READ SOURCEDATA.READ ORGANIZATION.READ ORGANIZATION.CREATE ORGANIZATION.UPDATE"` | |
52+
| oauth_clients[0].scope | string | `"SOURCEDATA.CREATE SOURCETYPE.UPDATE SOURCETYPE.DELETE AUTHORITY.UPDATE MEASUREMENT.DELETE PROJECT.READ AUDIT.CREATE USER.DELETE AUTHORITY.DELETE SUBJECT.DELETE MEASUREMENT.UPDATE SOURCEDATA.UPDATE SUBJECT.READ USER.UPDATE SOURCETYPE.CREATE AUTHORITY.READ USER.CREATE SOURCE.CREATE SOURCE.READ SUBJECT.CREATE ROLE.UPDATE ROLE.READ MEASUREMENT.READ PROJECT.UPDATE PROJECT.DELETE ROLE.DELETE SOURCE.DELETE SOURCETYPE.READ ROLE.CREATE SOURCEDATA.DELETE SUBJECT.UPDATE SOURCE.UPDATE PROJECT.CREATE AUDIT.READ MEASUREMENT.CREATE AUDIT.DELETE AUDIT.UPDATE AUTHORITY.CREATE USER.READ SOURCEDATA.READ ORGANIZATION.READ ORGANIZATION.CREATE ORGANIZATION.UPDATE OAUTHCLIENTS.READ OAUTHCLIENTS.CREATE OAUTHCLIENTS.UPDATE"` | |
5353
| oauth_clients[0].audience[0] | string | `"res_ManagementPortal"` | |
5454
| oauth_clients[0].allowed_cors_origins[0] | string | `"http://localhost:3000"` | |
5555
| oauth_clients[0].skip_consent | bool | `true` | |

charts/radar-hydra/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ oauth_clients:
9898
responseTypes:
9999
- code
100100
- id_token
101-
scope: SOURCEDATA.CREATE SOURCETYPE.UPDATE SOURCETYPE.DELETE AUTHORITY.UPDATE MEASUREMENT.DELETE PROJECT.READ AUDIT.CREATE USER.DELETE AUTHORITY.DELETE SUBJECT.DELETE MEASUREMENT.UPDATE SOURCEDATA.UPDATE SUBJECT.READ USER.UPDATE SOURCETYPE.CREATE AUTHORITY.READ USER.CREATE SOURCE.CREATE SOURCE.READ SUBJECT.CREATE ROLE.UPDATE ROLE.READ MEASUREMENT.READ PROJECT.UPDATE PROJECT.DELETE ROLE.DELETE SOURCE.DELETE SOURCETYPE.READ ROLE.CREATE SOURCEDATA.DELETE SUBJECT.UPDATE SOURCE.UPDATE PROJECT.CREATE AUDIT.READ MEASUREMENT.CREATE AUDIT.DELETE AUDIT.UPDATE AUTHORITY.CREATE USER.READ SOURCEDATA.READ ORGANIZATION.READ ORGANIZATION.CREATE ORGANIZATION.UPDATE
101+
scope: SOURCEDATA.CREATE SOURCETYPE.UPDATE SOURCETYPE.DELETE AUTHORITY.UPDATE MEASUREMENT.DELETE PROJECT.READ AUDIT.CREATE USER.DELETE AUTHORITY.DELETE SUBJECT.DELETE MEASUREMENT.UPDATE SOURCEDATA.UPDATE SUBJECT.READ USER.UPDATE SOURCETYPE.CREATE AUTHORITY.READ USER.CREATE SOURCE.CREATE SOURCE.READ SUBJECT.CREATE ROLE.UPDATE ROLE.READ MEASUREMENT.READ PROJECT.UPDATE PROJECT.DELETE ROLE.DELETE SOURCE.DELETE SOURCETYPE.READ ROLE.CREATE SOURCEDATA.DELETE SUBJECT.UPDATE SOURCE.UPDATE PROJECT.CREATE AUDIT.READ MEASUREMENT.CREATE AUDIT.DELETE AUDIT.UPDATE AUTHORITY.CREATE USER.READ SOURCEDATA.READ ORGANIZATION.READ ORGANIZATION.CREATE ORGANIZATION.UPDATE OAUTHCLIENTS.READ OAUTHCLIENTS.CREATE OAUTHCLIENTS.UPDATE
102102
audience:
103103
- res_ManagementPortal
104104
allowed_cors_origins:

charts/radar-kratos/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ Consult the [documentation](https://artifacthub.io/packages/helm/ory/kratos) of
4545
| kratos.advertised_protocol | string | `"https"` | Protocol for the Kratos service (allowed values: http, https) |
4646
| kratos.kratos.automigration | object | `{"enabled":true}` | Enables database migration |
4747
| kratos.kratos.identitySchemas | object | `{"identity.schema.admin.json":"{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"admin\",\n \"title\": \"admin\",\n \"type\": \"object\",\n \"properties\": {\n \"traits\": {\n \"type\": \"object\",\n \"properties\": {\n \"email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"title\": \"E-Mail\",\n \"minLength\": 5,\n \"ory.sh/kratos\": {\n \"credentials\": {\n \"password\": {\n \"identifier\": true\n },\n \"totp\": {\n \"account_name\": true\n }\n },\n \"verification\": {\n \"via\": \"email\"\n },\n \"recovery\": {\n \"via\": \"email\"\n }\n }\n }\n },\n \"required\": [\"email\"]\n }\n },\n \"additionalProperties\": false\n}\n","identity.schema.researcher.json":"{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"user\",\n \"title\": \"user\",\n \"type\": \"object\",\n \"properties\": {\n \"traits\": {\n \"type\": \"object\",\n \"properties\": {\n \"email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"title\": \"E-Mail\",\n \"minLength\": 5,\n \"ory.sh/kratos\": {\n \"credentials\": {\n \"password\": {\n \"identifier\": true\n },\n \"totp\": {\n \"account_name\": true\n }\n },\n \"verification\": {\n \"via\": \"email\"\n },\n \"recovery\": {\n \"via\": \"email\"\n }\n }\n }\n },\n \"required\": [\"email\"]\n }\n },\n \"additionalProperties\": false\n}\n","identity.schema.subject.json":"{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"subject\",\n \"title\": \"subject\",\n \"type\": \"object\",\n \"properties\": {\n \"traits\": {\n \"type\": \"object\",\n \"properties\": {\n \"email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"title\": \"E-Mail\",\n \"minLength\": 5,\n \"ory.sh/kratos\": {\n \"credentials\": {\n \"password\": {\n \"identifier\": true\n },\n \"totp\": {\n \"account_name\": true\n }\n },\n \"verification\": {\n \"via\": \"email\"\n },\n \"recovery\": {\n \"via\": \"email\"\n }\n }\n }\n },\n \"required\": [\"email\"]\n }\n },\n \"additionalProperties\": false\n}\n"}` | You can add multiple identity schemas here. You can pass JSON schema using `--set-file` Helm CLI argument. |
48+
| kratos.kratos.config.session.whoami.tokenizer | object | `{"enabled":true,"templates":{"mp_jwt_template":{"claims_mapper_url":"file:///etc/kratos/claims-mapper.jsonnet","jwks_url":"file:///etc/kratos/jwks.json","ttl":"1h"}}}` | Session-to-JWT token exchange configuration This enables Kratos to convert session tokens to JWT tokens for ManagementPortal integration |
49+
| kratos.kratos.config.session.whoami.tokenizer.templates.mp_jwt_template.ttl | string | `"1h"` | JWT token time-to-live |
50+
| kratos.kratos.config.session.whoami.tokenizer.templates.mp_jwt_template.claims_mapper_url | string | `"file:///etc/kratos/claims-mapper.jsonnet"` | JWT claims mapper URL pointing to JsonNet template |
51+
| keystore | string | `""` | Keystore for JWT signing |
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ template "common.names.fullname" . }}-claims-mapper
5+
namespace: {{ include "common.names.namespace" . | quote }}
6+
labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
7+
{{- if .Values.commonAnnotations }}
8+
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
9+
{{- end }}
10+
data:
11+
claims-mapper.jsonnet: |
12+
local claims = std.extVar("claims");
13+
local session = std.extVar("session");
14+
15+
{
16+
claims: {
17+
// Standard JWT claims
18+
iss: claims.iss,
19+
sub: claims.sub,
20+
exp: claims.exp,
21+
jti: claims.jti,
22+
sid: claims.sid,
23+
nbf: claims.nbf,
24+
iat: claims.iat,
25+
aud: "res_ManagementPortal",
26+
27+
// Custom claims
28+
authorities: if std.objectHas(session.identity, "metadata_public") &&
29+
std.objectHas(session.identity.metadata_public, "authorities") &&
30+
session.identity.metadata_public.authorities != null
31+
then session.identity.metadata_public.authorities
32+
else [],
33+
34+
roles: if std.objectHas(session.identity, "metadata_public") &&
35+
std.objectHas(session.identity.metadata_public, "roles") &&
36+
session.identity.metadata_public.roles != null
37+
then session.identity.metadata_public.roles
38+
else [],
39+
40+
scope: if std.objectHas(session.identity, "metadata_public") &&
41+
std.objectHas(session.identity.metadata_public, "scope") &&
42+
session.identity.metadata_public.scope != null
43+
then std.join(" ", session.identity.metadata_public.scope)
44+
else "",
45+
46+
sources: if std.objectHas(session.identity, "metadata_public") &&
47+
std.objectHas(session.identity.metadata_public, "sources") &&
48+
session.identity.metadata_public.sources != null
49+
then session.identity.metadata_public.sources
50+
else [],
51+
52+
grant_type: if std.objectHas(session.identity, "metadata_public") &&
53+
std.objectHas(session.identity.metadata_public, "grant_type") &&
54+
session.identity.metadata_public.grant_type != null
55+
then session.identity.metadata_public.grant_type
56+
else "password",
57+
58+
client_id: if std.objectHas(session.identity, "metadata_public") &&
59+
std.objectHas(session.identity.metadata_public, "client_id") &&
60+
session.identity.metadata_public.client_id != null
61+
then session.identity.metadata_public.client_id
62+
else "",
63+
64+
user_name: if std.objectHas(session.identity, "metadata_public") &&
65+
std.objectHas(session.identity.metadata_public, "mp_login") &&
66+
session.identity.metadata_public.mp_login != null
67+
then session.identity.metadata_public.mp_login
68+
else if std.objectHas(session.identity.traits, "email")
69+
then session.identity.traits.email
70+
else ""
71+
}
72+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: {{ template "common.names.fullname" . }}-keystore
5+
namespace: {{ include "common.names.namespace" . | quote }}
6+
labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
7+
{{- if .Values.commonAnnotations }}
8+
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
9+
{{- end }}
10+
type: Opaque
11+
data:
12+
jwks.json: {{ .Values.keystore }}

charts/radar-kratos/values.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ kratos:
172172
cookie:
173173
## -- If false, cookie is removed when the browser is closed --##
174174
persistent: false
175+
whoami:
176+
# -- Session-to-JWT token exchange configuration
177+
# This enables Kratos to convert session tokens to JWT tokens for ManagementPortal integration
178+
tokenizer:
179+
enabled: true
180+
templates:
181+
mp_jwt_template:
182+
# # -- JSON keystore file path for JWT signing keys
183+
jwks_url: "file:///etc/kratos/jwks.json"
184+
# -- JWT token time-to-live
185+
ttl: 1h
186+
# -- JWT claims mapper URL pointing to JsonNet template
187+
claims_mapper_url: "file:///etc/kratos/claims-mapper.jsonnet"
175188

176189
courier:
177190
smtp:
@@ -343,6 +356,20 @@ kratos:
343356
key: uri
344357
- name: DSN
345358
value: "$(POSTGRES_URI)?sslmode=disable&max_conns=20&max_idle_conns=4"
359+
extraVolumes:
360+
- name: keystore
361+
secret:
362+
secretName: radar-kratos-keystore
363+
- name: claims-mapper
364+
configMap:
365+
name: radar-kratos-claims-mapper
366+
extraVolumeMounts:
367+
- name: keystore
368+
mountPath: /etc/kratos/jwks.json
369+
subPath: jwks.json
370+
- name: claims-mapper
371+
mountPath: /etc/kratos/claims-mapper.jsonnet
372+
subPath: claims-mapper.jsonnet
346373
statefulSet:
347374
extraEnv:
348375
- name: POSTGRES_URI
@@ -352,6 +379,20 @@ kratos:
352379
key: uri
353380
- name: DSN
354381
value: "$(POSTGRES_URI)?sslmode=disable&max_conns=20&max_idle_conns=4"
382+
extraVolumes:
383+
- name: keystore
384+
secret:
385+
secretName: radar-kratos-keystore
386+
- name: claims-mapper
387+
configMap:
388+
name: radar-kratos-claims-mapper
389+
extraVolumeMounts:
390+
- name: keystore
391+
mountPath: /etc/kratos/keystore.jks
392+
subPath: keystore.jks
393+
- name: claims-mapper
394+
mountPath: /etc/kratos/claims-mapper.jsonnet
395+
subPath: claims-mapper.jsonnet
355396
job:
356397
extraEnv:
357398
- name: POSTGRES_URI
@@ -371,3 +412,9 @@ kratos:
371412
key: uri
372413
- name: DSN
373414
value: "$(POSTGRES_URI)?sslmode=disable&max_conns=20&max_idle_conns=4"
415+
416+
# -- Keystore for JWT signing
417+
keystore: ""
418+
# With helmfile, this can be set in a production.yaml.gotmpl
419+
# file by setting
420+
# keystore: {{ readFile "../etc/radar-kratos/jwks.json" }}

0 commit comments

Comments
 (0)