big-night-72627
04/07/2025, 5:28 PMbrief-egg-61061
04/07/2025, 5:28 PMbrief-egg-61061
04/07/2025, 5:29 PMjavascript import { Configuration, FrontendApi, LoginFlow, UiNode, UiNodeInputAttributes, } from "@ory/client" import { filterNodesByGroups, isUiNodeInputAttributes, } from "@ory/integrations/ui" import { useEffect, useState } from "react" import { useSearchParams } from "react-router-dom" const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", // Use your local Ory Tunnel URL baseOptions: { withCredentials: true, // we need to include cookies }, }), ) export const Login = () => { const [flow, setFlow] = useState<LoginFlow>() const [searchParams] = useSearchParams() useEffect(() => { // check if the login flow is for two factor authentication const aal2 = searchParams.get("aal2") // we can redirect the user back to the page they were on before login const returnTo = searchParams.get("return_to") frontend .createBrowserLoginFlow({ returnTo: returnTo || "/", // redirect to the root path after login // if the user has a session, refresh it refresh: true, // if the aal2 query parameter is set, we get the two factor login flow UI nodes aal: aal2 ? "aal2" : "aal1", }) .then(({ data: flow }) => { // set the flow data setFlow(flow) }) .catch((err) => { // Couldn't create login flow // handle the error }) }, []) const mapUINode = (node: UiNode, key: number) => { // other node types are also supported if (isUiNodeInputAttributes(node.attributes)) { const attrs = node.attributes as UiNodeInputAttributes const nodeType = attrs.type switch (nodeType) { case "button": case "submit": return ( <button type={attrs.type as "submit" | "reset" | "button" | undefined} name={attrs.name} value={attrs.value} /> ) default: return ( <input name={attrs.name} type={attrs.type} autoComplete={ attrs.autocomplete || attrs.name === "identifier" ? "username" : "" } defaultValue={attrs.value} required={attrs.required} disabled={attrs.disabled} /> ) } } } return flow ? ( <form action={flow.ui.action} method={flow.ui.method}> {filterNodesByGroups({ nodes: flow.ui.nodes, // we will also map default fields here such as csrf_token // modify this to include "code" for passwordless login groups: ["default", "code"], }).map((node, idx) => mapUINode(node, idx))} </form> ) : ( <div>Loading...</div> ) }
### 2. For WebAuthn/Passkey Support For WebAuthn (passkeys), you need to include the WebAuthn script in your page: html <head> <script src="/.well-known/ory/webauthn.js"></script> </head>
Then modify your login flow to include the "webauthn" group: javascript // In your return statement return flow ? ( <form action={flow.ui.action} method={flow.ui.method}> {filterNodesByGroups({ nodes: flow.ui.nodes, // Include webauthn for passwordless authentication groups: ["default", "webauthn"], }).map((node, idx) => mapUINode(node, idx))} </form> ) : ( <div>Loading...</div> )
## Creating a Passwordless Registration Flow For registration with passwordless options: javascript import { Configuration, FrontendApi } from "@ory/client" const frontend = new FrontendApi( new Configuration({ basePath: `https://${process.env.ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`, }), ) export async function createRegistration() { return await frontend.createBrowserRegistrationFlow() }
Then in your registration component, filter for the passwordless methods: javascript return flow ? ( <form action={flow.ui.action} method={flow.ui.method}> {filterNodesByGroups({ nodes: flow.ui.nodes, // Include code or webauthn for passwordless registration groups: ["default", "code", "webauthn"], }).map((node, idx) => mapUINode(node, idx))} </form> ) : ( <div>Loading...</div> )
## Configuration To enable passwordless authentication in your Ory project, you need to configure it in your Ory configuration: yaml selfservice: methods: code: enabled: true passwordless_enabled: true webauthn: enabled: true config: passwordless: true rp: display_name: Your App Name id: <http://your-domain.com|your-domain.com> origins: - <https://your-domain.com> registration: after: code: hooks: - hook: session # This automatically logs users in after registration
For a unified sign-in/sign-up experience with passwordless authentication, you can create a custom UI that: 1. Initially shows only an email input field 2. Checks if the email exists 3. Triggers either login or registration flow based on email existence 4. Shows the OTP input field for code entry This approach allows for a seamless experience where users don't need to know if they already have an account or not. [Ory Identities](https://www.ory.sh/docs/identities) provides these passwordless authentication methods as part of its comprehensive identity management solution. Let me know if you need more specific details about any part of this implementation!big-night-72627
04/07/2025, 5:56 PMbrief-egg-61061
04/07/2025, 5:56 PMbrief-egg-61061
04/07/2025, 5:57 PMjavascript import { Configuration, FrontendApi, RegistrationFlow, UiNodeInputAttributes, } from "@ory/client" import { filterNodesByGroups, isUiNodeInputAttributes, } from "@ory/integrations/ui" import { useEffect, useState } from "react" // Initialize the Ory Frontend API client const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", // Use your Ory Tunnel URL or Ory Network project URL baseOptions: { withCredentials: true, // Important for cookies }, }), ) function PasswordlessRegistration() { const [flow, setFlow] = useState(null) const [error, setError] = useState(null) // Initialize the registration flow useEffect(() => { // Create a new registration flow frontend.createBrowserRegistrationFlow() .then(({ data }) => { setFlow(data) }) .catch((err) => { setError(err.response?.data || err) console.error(err) }) }, []) // Handle form submission const handleSubmit = (e) => { e.preventDefault() const form = e.currentTarget const formData = new FormData(form) // Submit the form data to Ory frontend.updateRegistrationFlow({ flow: flow.id, updateRegistrationFlowBody: Object.fromEntries(formData) }) .then(({ data }) => { // If successful, the user will be redirected // If additional steps are needed (like entering the code), update the flow setFlow(data) }) .catch((err) => { setError(err.response?.data || err) console.error(err) }) } if (error) { return <div>An error occurred: {JSON.stringify(error)}</div> } if (!flow) { return <div>Loading...</div> } return ( <div> <h2>Register with Passwordless Authentication</h2> <form action={flow.ui.action} method={flow.ui.method} onSubmit={handleSubmit}> {/* Render all nodes from the "code" group and default group */} {filterNodesByGroups({ nodes: flow.ui.nodes, groups: ["default", "code"], }).map((node, idx) => { if (isUiNodeInputAttributes(node.attributes)) { const attrs = node.attributes // Display messages for this node if any const messages = node.messages.length > 0 && ( <div className="messages"> {node.messages.map((message, i) => ( <div key={i} className={`message ${message.type}`}> {message.text} </div> ))} </div> ) // Display label if available const label = node.meta?.label?.text && ( <label htmlFor={attrs.name}>{node.meta.label.text}</label> ) // Render different input types switch (attrs.type) { case "submit": return ( <div key={idx}> <button type="submit" name={attrs.name} value={attrs.value || ""} disabled={attrs.disabled} > {node.meta?.label?.text || "Submit"} </button> {messages} </div> ) case "hidden": return ( <input key={idx} type={attrs.type} name={attrs.name} value={attrs.value || ""} /> ) default: return ( <div key={idx}> {label} <input id={attrs.name} name={attrs.name} type={attrs.type} value={attrs.value || ""} required={attrs.required} disabled={attrs.disabled} placeholder={node.meta?.label?.text} /> {messages} </div> ) } } return null })} </form> </div> ) } export default PasswordlessRegistration
## Configuration for Passwordless Registration To enable passwordless registration in your Ory project, you need to configure it properly. Here's the configuration you should add to your Ory configuration file: yaml selfservice: methods: code: enabled: true passwordless_enabled: true # Enable passwordless login with code config: lifespan: 15m0s # How long the code is valid flows: registration: enabled: true after: code: hooks: - hook: session # Automatically log users in after registration
## How the Passwordless Registration Flow Works 1. The user visits your registration page 2. Your application initializes a registration flow with Ory 3. The user enters their email address 4. The form is submitted to Ory 5. Ory sends a one-time code to the user's email 6. The user enters the code in your application 7. Upon successful verification, the user is registered and (if configured) automatically logged in ## Important Notes 1. For this to work, you need to have the "code" method enabled in your Ory configuration. 2. The after.code.hooks
configuration with the session
hook ensures users are automatically logged in after successful registration. 3. Two-step registration is now the default in Ory Kratos v1.2.0 and later [as mentioned in this discussion](https://github.com/ory/kratos/discussions/4025). If you want to create a unified experience where users don't need to know if they already have an account, you can implement a custom UI that checks if the email exists and then triggers either login or registration as appropriate, as [discussed here](https://github.com/ory/kratos/discussions/4294). For more information about self-service flows in Ory, you can refer to the [Self-service flows documentation](https://www.ory.sh/docs/kratos/self-service).big-night-72627
04/09/2025, 6:01 PMbrief-egg-61061
04/09/2025, 6:01 PMbrief-egg-61061
04/09/2025, 6:02 PMjavascript import { Configuration, FrontendApi, RegistrationFlow, UiNodeInputAttributes, UiNodeScriptAttributes, } from "@ory/client" import { filterNodesByGroups, isUiNodeInputAttributes, } from "@ory/integrations/ui" import { HTMLAttributeReferrerPolicy, useEffect, useState } from "react" // Initialize the Ory Frontend API client const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", // Use your Ory Tunnel URL or Ory Network project URL baseOptions: { withCredentials: true, }, }), ) function PasskeyRegistration() { const [flow, setFlow] = useState(null) const [error, setError] = useState(null) // Initialize the registration flow useEffect(() => { frontend.createBrowserRegistrationFlow() .then(({ data }) => { setFlow(data) }) .catch((err) => { setError(err.response?.data || err) console.error(err) }) }, []) // Add the WebAuthn script to the DOM useEffect(() => { if (!flow) return // Add the WebAuthn script const scriptNodes = filterNodesByGroups({ nodes: flow.ui.nodes, groups: "passkey", attributes: "text/javascript", withoutDefaultGroup: true, withoutDefaultAttributes: true, }).map((node) => { const attr = node.attributes const script = document.createElement("script") script.src = attr.src script.type = attr.type script.async = attr.async script.referrerPolicy = attr.referrerpolicy script.crossOrigin = attr.crossorigin script.integrity = attr.integrity document.body.appendChild(script) return script }) // Check for onloadTrigger const onloadNode = flow.ui.nodes.find( (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.onloadTrigger === "oryPasskeyRegistration" ) if (onloadNode) { // Execute the passkey registration trigger when available window.setTimeout(() => { if (window.__oryPasskeyRegistration) { window.__oryPasskeyRegistration() } }, 500) } // Cleanup return () => { scriptNodes.forEach((script) => { document.body.removeChild(script) }) } }, [flow]) if (error) { return <div>An error occurred: {JSON.stringify(error)}</div> } if (!flow) { return <div>Loading...</div> } return ( <div> <h2>Register with Passkey</h2> <form action={flow.ui.action} method={flow.ui.method}> {/* Render all nodes from the "passkey" group and default group */} {filterNodesByGroups({ nodes: flow.ui.nodes, groups: ["default", "passkey"], attributes: ["hidden", "submit", "button"], }).map((node, idx) => { if (isUiNodeInputAttributes(node.attributes)) { const attrs = node.attributes // Display messages for this node if any const messages = node.messages.length > 0 && ( <div className="messages"> {node.messages.map((message, i) => ( <div key={i} className={`message ${message.type}`}> {message.text} </div> ))} </div> ) // Display label if available const label = node.meta?.label?.text && ( <label htmlFor={attrs.name}>{node.meta.label.text}</label> ) // Render different input types switch (attrs.type) { case "button": case "submit": return ( <div key={idx}> <button type={attrs.type} name={attrs.name} value={attrs.value || ""} disabled={attrs.disabled} onClick={attrs.onclick ? () => eval(attrs.onclick) : undefined} > {node.meta?.label?.text || "Submit"} </button> {messages} </div> ) default: return ( <div key={idx}> {label} <input id={attrs.name} name={attrs.name} type={attrs.type} value={attrs.value || ""} required={attrs.required} disabled={attrs.disabled} /> {messages} </div> ) } } return null })} </form> </div> ) } export default PasskeyRegistration
## Passkey Login Flow Here's a complete React component for passkey login: javascript import { Configuration, FrontendApi, LoginFlow, UiNodeInputAttributes, } from "@ory/client" import { filterNodesByGroups, isUiNodeInputAttributes, } from "@ory/integrations/ui" import { useEffect, useState } from "react" // Initialize the Ory Frontend API client const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", // Use your Ory Tunnel URL or Ory Network project URL baseOptions: { withCredentials: true, }, }), ) function PasskeyLogin() { const [flow, setFlow] = useState(null) const [error, setError] = useState(null) // Initialize the login flow useEffect(() => { frontend.createBrowserLoginFlow({ refresh: true, // Refresh the session if it exists }) .then(({ data }) => { setFlow(data) }) .catch((err) => { setError(err.response?.data || err) console.error(err) }) }, []) // Add the WebAuthn script to the DOM useEffect(() => { if (!flow) return // Add the WebAuthn script const scriptNodes = filterNodesByGroups({ nodes: flow.ui.nodes, groups: "passkey", attributes: "text/javascript", withoutDefaultGroup: true, withoutDefaultAttributes: true, }).map((node) => { const attr = node.attributes const script = document.createElement("script") script.src = attr.src script.type = attr.type script.async = attr.async script.referrerPolicy = attr.referrerpolicy script.crossOrigin = attr.crossorigin script.integrity = attr.integrity document.body.appendChild(script) return script }) // Check for onloadTrigger for auto-initialization const onloadNode = flow.ui.nodes.find( (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.onloadTrigger === "oryPasskeyLoginAutocompleteInit" ) if (onloadNode) { // Execute the passkey login autocomplete trigger when available window.setTimeout(() => { if (window.__oryPasskeyLoginAutocompleteInit) { window.__oryPasskeyLoginAutocompleteInit() } }, 500) } // Cleanup return () => { scriptNodes.forEach((script) => { document.body.removeChild(script) }) } }, [flow]) if (error) { return <div>An error occurred: {JSON.stringify(error)}</div> } if (!flow) { return <div>Loading...</div> } return ( <div> <h2>Login with Passkey</h2> <form action={flow.ui.action} method={flow.ui.method}> {/* Render all nodes from the "passkey" group and default group */} {filterNodesByGroups({ nodes: flow.ui.nodes, groups: ["default", "passkey"], }).map((node, idx) => { if (isUiNodeInputAttributes(node.attributes)) { const attrs = node.attributes // Display messages for this node if any const messages = node.messages.length > 0 && ( <div className="messages"> {node.messages.map((message, i) => ( <div key={i} className={`message ${message.type}`}> {message.text} </div> ))} </div> ) // Display label if available const label = node.meta?.label?.text && ( <label htmlFor={attrs.name}>{node.meta.label.text}</label> ) // Render different input types switch (attrs.type) { case "button": case "submit": return ( <div key={idx}> <button type={attrs.type} name={attrs.name} value={attrs.value || ""} disabled={attrs.disabled} onClick={attrs.onclick ? () => eval(attrs.onclick) : undefined} > {node.meta?.label?.text || "Sign in with passkey"} </button> {messages} </div> ) default: return ( <div key={idx}> {label} <input id={attrs.name} name={attrs.name} type={attrs.type} value={attrs.value || ""} required={attrs.required} disabled={attrs.disabled} /> {messages} </div> ) } } return null })} </form> </div> ) } export default PasskeyLogin
## HTML Setup Make sure to include the WebAuthn script in your HTML: html <head> <script src="/.well-known/ory/webauthn.js"></script> </head>
## Ory Configuration To enable passkey authentication in your Ory project, add this to your configuration: yaml selfservice: methods: passkey: enabled: true config: rp: display_name: "Your Application Name" id: "<http://your-domain.com|your-domain.com>" origins: - "<https://your-domain.com>" flows: registration: after: passkey: hooks: - hook: session # Automatically log users in after registration
## How Passkey Authentication Works 1. Registration: - User initiates registration - Browser prompts user to create a passkey - User authenticates with biometrics (FaceID, TouchID, Windows Hello, etc.) - A new credential is created and stored 2. Login: - User clicks "Sign in with passkey" - Browser prompts user to select a passkey - User authenticates with biometrics - User is logged in Passkeys provide a morebig-night-72627
04/10/2025, 8:15 PMbrief-egg-61061
04/10/2025, 8:15 PMbrief-egg-61061
04/10/2025, 8:15 PMhtml <head> <script src="/.well-known/ory/webauthn.js"></script> </head>
2. Check for script loading errors: Open your browser's developer console and check if there are any errors related to loading the script. The URL might be incorrect or the script might be blocked. 3. Use the correct function name: Make sure you're using the correct function name. In newer versions of Ory, the function might be named __oryPasskeyRegistration
(with double underscores) instead of oryPasskeyRegistration
. 4. Wait for script to load: Add a check to ensure the script is fully loaded before trying to use the function: javascript // Add this to your component useEffect(() => { const checkWebAuthnLoaded = setInterval(() => { if (window.__oryPasskeyRegistration) { clearInterval(checkWebAuthnLoaded); console.log("WebAuthn script loaded successfully"); } }, 100); // Clear interval after 10 seconds to prevent infinite checking setTimeout(() => clearInterval(checkWebAuthnLoaded), 10000); return () => clearInterval(checkWebAuthnLoaded); }, []);
5. Check for trigger attributes: Make sure your UI nodes have the correct onclickTrigger
or onloadTrigger
attributes. The error might occur if you're trying to call the function directly instead of letting Ory's UI nodes handle it. 6. Verify Ory version compatibility: Ensure you're using the correct version of the Ory client that matches your Ory server version. The function names and behavior might differ between versions. If you're still experiencing issues, you might want to try using the onclickTrigger
approach instead of directly calling the function: javascript // Instead of directly calling window.oryPasskeyRegistration() // Use the UI node with the correct trigger const passkeyButton = flow.ui.nodes.find( node => isUiNodeInputAttributes(node.attributes) && node.attributes.onclickTrigger === "oryPasskeyRegistration" ); // Then render this button in your UI
This approach lets Ory handle the function calling internally, which can be more reliable.big-night-72627
04/10/2025, 8:20 PMThis browser does not support WebAuthn!
using Chromebrief-egg-61061
04/10/2025, 8:20 PMbrief-egg-61061
04/10/2025, 8:21 PM# For Passkeys ory patch identity-config --project <project-id> --workspace <workspace-id> \ --replace "/selfservice/methods/passkey/config/rp/id=\"localhost\"" \ --replace "/selfservice/methods/passkey/config/rp/origins=[\"<http://localhost:3000\>"]" \ --format yaml
2. HTTPS requirement: WebAuthn typically requires HTTPS in production environments. For local development, it only works on localhost
or with properly configured HTTPS. 3. Browser settings: Make sure you haven't disabled WebAuthn in your Chrome settings. 4. Chrome version: Ensure you're using a recent version of Chrome that fully supports WebAuthn. 5. Secure context: WebAuthn only works in secure contexts (HTTPS or localhost). 6. Cross-origin issues: If you're using iframes or cross-origin requests, this could cause WebAuthn to fail. To further diagnose the issue, check your browser console for additional error messages that might provide more specific information about why WebAuthn is failing. If you're testing locally, make sure you're using localhost
as your domain, as this is one of the few non-HTTPS domains where WebAuthn is allowed to work.big-night-72627
04/14/2025, 9:30 PMbrief-egg-61061
04/14/2025, 9:30 PMbrief-egg-61061
04/14/2025, 9:30 PMsessions.cookie.persistent
is enabled. If you can provide more context about your specific implementation and where exactly this error is occurring, I can give you more targeted troubleshooting steps.cold-winter-87802
04/20/2025, 10:37 PMbrief-egg-61061
04/20/2025, 10:37 PMbrief-egg-61061
04/20/2025, 10:37 PMreturn_to
Query Parameter The simplest way to set a redirect URL for a specific login attempt is to use the return_to
query parameter when initializing the login flow: javascript // When initializing the login flow frontend.createBrowserLoginFlow({ return_to: "<https://your-app.com/dashboard>" })
This approach works for any authentication method, including passkeys, as described in the [Browser redirects and flow completion documentation](https://www.ory.sh/docs/concepts/redirects). ### 2. Configuration-Based Redirects You can also configure default redirects in your Ory configuration: yaml selfservice: flows: login: after: default_browser_return_url: <https://your-app.com/default-page> passkey: default_browser_return_url: <https://your-app.com/passkey-specific-page>
This configuration sets a specific redirect URL for passkey authentication that overrides the default redirect URL, as explained in the [HTTP redirection configuration documentation](https://www.ory.sh/docs/kratos/concepts/browser-redirect-flow-completion). ### 3. For API Clients (AJAX) If you're using an API client like AJAX instead of a traditional browser form submission, you'll need to handle the redirect in your code: javascript // After successful login .then((res) => { router.push('/your-redirect-path') })
As mentioned in the [troubleshooting documentation for API clients](https://www.ory.sh/docs/concepts/redirects#api-clients), browser redirects only work for regular browser requests, not for API clients. ### 4. Allowed Return URLs For security reasons, Ory prevents open redirect attacks by validating redirect URLs against an allow list. Make sure your redirect domain is in the allowed list: yaml selfservice: allowed_return_urls: - <https://your-app.com/>
This is important when using dynamic redirects with the return_to
parameter, as explained in the [allow list documentation](https://www.ory.sh/docs/concepts/redirects#allow-list). ## Important Notes 1. Make sure your redirect URLs are valid with a proper scheme (HTTP or HTTPS). 2. You cannot set Ory-owned domains as redirect URLs. 3. For local development, make sure your configuration matches your testing environment. If you're experiencing issues with redirects after passkey authentication, check that your configuration is correct and that you're handling the response appropriately based on whether you're using browser forms or API clients.cold-winter-87802
04/20/2025, 10:38 PMbrief-egg-61061
04/20/2025, 10:38 PMbrief-egg-61061
04/20/2025, 10:39 PMjavascript import { Configuration, FrontendApi } from "@ory/client" const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", // Use your Ory Tunnel URL or project URL baseOptions: { withCredentials: true, }, }), ) // Create a logout flow and get the logout URL async function logout() { try { // Create a "logout flow" in Ory const { data: flow } = await frontend.createBrowserLogoutFlow() // Option 1: Redirect to the logout URL window.location.href = flow.logout_url // Option 2: Use the token to perform the logout via AJAX // await frontend.updateLogoutFlow({ // token: flow.logout_token, // }) // Then redirect or update UI as needed } catch (error) { console.error("Logout failed:", error) } }
This approach works for any authentication method, including passkeys, as shown in the [Kratos Self-service Flows documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-logout#single-page-application-spa). ### 2. React Example with Logout Button javascript import { Configuration, FrontendApi } from "@ory/client" import { useState } from "react" const frontend = new FrontendApi( new Configuration({ basePath: "<http://localhost:4000>", baseOptions: { withCredentials: true, }, }), ) export function LogoutButton() { const [logoutUrl, setLogoutUrl] = useState(null) // Get the logout URL when component mounts useEffect(() => { frontend.createBrowserLogoutFlow() .then(({ data }) => { setLogoutUrl(data.logout_url) }) .catch(err => console.error(err)) }, []) return ( <a href={logoutUrl}>Logout</a> ) }
This example is similar to the approach shown in the [React integration documentation](https://www.ory.sh/docs/getting-started/integrate-auth/react#require-login-to-access-the-app-page). ## Native/API Applications If you're using passkeys in a native application or API client that uses session tokens: javascript import { Configuration, FrontendApi } from "@ory/client" const frontend = new FrontendApi( new Configuration({ basePath: `https://${process.env.ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`, }), ) export async function logout(token) { return await frontend.performNativeLogout({ performNativeLogoutBody: { session_token: token, }, }) }
This approach is documented in the [Native API applications section](https://www.ory.sh/docs/kratos/self-service/flows/user-logout#native-api-applications) of the Ory documentation. ## OAuth2/OpenID Connect Logout If you're using Ory OAuth2 & OpenID Connect with passkeys, you'll need to use the OIDC logout endpoint: javascript // Redirect to the OIDC logout endpoint window.location.href = `https://${projectSlug}.<http://projects.oryapis.com/oauth2/sessions/logout|projects.oryapis.com/oauth2/sessions/logout>`
This endpoint initiates and completes user logout at the Ory OAuth2 & OpenID provider as described in the [OpenID Connect logout documentation](https://www.ory.sh/docs/oauth2-oidc/oidc-logout). ## Important Notes 1. The logout process is the same regardless of which authentication method (passkeys, passwords, etc.) was used to log in. 2. For browser applications, make sure to handle cookies properly by including withCredentials: true
. 3. For native applications, you need to store and provide the session token. 4. You can configure post-logout redirects in your Ory configuration or by using the return_to
parameter. Let me know if you need more specific implementation details for your particular use case!cold-winter-87802
04/20/2025, 11:50 PMbrief-egg-61061
04/20/2025, 11:50 PMbrief-egg-61061
04/20/2025, 11:51 PMyaml selfservice: flows: login: before: hooks: - hook: web_hook config: url: <https://your-api.com/validate-domain> method: POST auth: type: api_key config: name: Authorization value: Bearer your-secret-token in: header
This configuration sends login data to your webhook endpoint before processing the login flow, as described in the [Hook Documentation](https://www.ory.sh/docs/kratos/hooks/configure-hooks). ### 2. Implementing the Webhook Logic Your webhook endpoint should validate the email domain and return an appropriate response: javascript // Example webhook implementation <http://app.post|app.post>('/validate-domain', (req, res) => { const { email } = req.body.traits || {}; if (!email) { return res.status(400).json({ error: { message: "Email is required" } }); } // Extract domain from email const domain = email.split('@')[1]; // List of allowed domains const allowedDomains = ['<http://company.com|company.com>', '<http://approved-partner.com|approved-partner.com>']; if (!allowedDomains.includes(domain)) { return res.status(403).json({ error: { message: "Your email domain is not authorized to access this application" } }); } // Allow the login to proceed return res.status(200).json({}); });
### 3. Flow-Interrupting Webhooks For more advanced control, you can use flow-interrupting webhooks that can completely block the login process: yaml selfservice: flows: login: before: hooks: - hook: web_hook config: url: <https://your-api.com/validate-domain> method: POST can_interrupt: true auth: type: api_key config: name: Authorization value: Bearer your-secret-token in: header
With can_interrupt: true
, your webhook can return a specific error response to prevent the login from proceeding, as explained in the [Flow-interrupting webhooks documentation](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#flow-interrupting-webhooks). ### 4. Using Ory CLI to Configure You can also set up these hooks using the Ory CLI: bash ory patch identity-config {project_id} \ --add '/selfservice/flows/login/before/hooks=[{"hook": "web_hook", "config": {"url": "<https://your-api.com/validate-domain>", "method": "POST", "can_interrupt": true}}]'
## Important Notes 1. This approach works for all authentication methods, including passkeys. 2. The webhook needs to be secure and reliable as it will be called for every login attempt. 3. For better user experience, consider showing clear error messages when domain validation fails. 4. You can implement more complex logic in your webhook, such as checking against a database of allowed domains or integrating with your organization's directory service. By implementing these domain restrictions, you can ensure that only users with approved email domains can authenticate to your application using passkeys or any other authentication method. [Ory Actions](https://www.ory.sh/docs/identities/sign-in/actions#customize-login-and-registration-behavior) provides a flexible way to customize login behavior, including domain restrictions and other validation rules.cold-winter-87802
04/20/2025, 11:54 PMbrief-egg-61061
04/20/2025, 11:54 PMbrief-egg-61061
04/20/2025, 11:54 PMyaml selfservice: flows: login: after: passkey: hooks: - hook: web_hook config: url: <https://your-api.com/update-traits> method: POST response: parse: true
With response.parse: true
, your webhook can return data that modifies the identity, as explained in the [Webhook response handling documentation](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#modify-identities). ## Webhook Implementation Your webhook should return a JSON response that includes the updated traits: javascript // Example webhook implementation <http://app.post|app.post>('/update-traits', (req, res) => { // Get the current identity from the request const { identity } = req.body; // Get the current traits const currentTraits = identity.traits || {}; // Append new data to traits const updatedTraits = { ...currentTraits, last_login_timestamp: new Date().toISOString(), login_count: (currentTraits.login_count || 0) + 1, // Add any other data you want to append }; // Return the updated identity res.status(200).json({ identity: { traits: updatedTraits } }); });
## Important Considerations 1. Complete Data Return: When updating traits, you must return the complete traits object, not just the fields you want to update. As mentioned in the [Modify identities documentation](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#modify-identities), if you only return partial data, the missing fields will be removed. 2. Schema Validation: Any traits you add must conform to your identity schema. Make sure your schema allows for the additional fields you're adding. 3. Metadata Alternative: For data that doesn't need to be user-editable, consider using identity metadata instead of traits: javascript res.status(200).json({ identity: { traits: currentTraits, // Keep existing traits metadata_public: { // Public metadata visible to the user last_login: new Date().toISOString() }, metadata_admin: { // Admin-only metadata login_statistics: { count: (identity.metadata_admin?.login_statistics?.count || 0) + 1 } } } });
This approach allows you to store additional information without modifying the user-editable traits, as described in the [Update identity metadata documentation](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#update-identity-metadata). By implementing these webhooks, you can effectively append data to user traits or metadata during passkey login flows.big-night-72627
05/03/2025, 3:40 PMbrief-egg-61061
05/03/2025, 3:40 PMbrief-egg-61061
05/03/2025, 3:41 PMhydra create client \ --endpoint <http://your-hydra-admin-api/> \ --id my-api-client \ --secret my-api-secret \ --grant-types client_credentials \ --response-types token \ --scope read,write
2. Your client ID and secret will function as your API key ## Using JSON Web Keys (JWKs) For more advanced scenarios, you can create JSON Web Keys (JWKs) that can be used for signing tokens: hydra create jwks --endpoint=<http://your-hydra-admin-api/> my-key-set --alg RS256
You can also set your own JSON Web Keys using the API: PUT /admin/keys/{set}
As described in the [Hydra API Reference](https://www.ory.sh/docs/hydra/reference/api), this allows you to save your own keys instead of letting Hydra generate them for you. ## Best Practices for Self-Hosted API Security 1. Protect admin APIs: Always place admin APIs behind proper authentication 2. Use HTTPS: Ensure all API communication happens over HTTPS 3. Rotate keys: Implement key rotation for security as described in the [Secrets and key rotation documentation](https://www.ory.sh/docs/hydra/self-hosted/secrets-key-rotation) 4. Use token prefixes: Ory uses specific prefixes for different token types (e.g., ory_at_
for access tokens) which helps with security scanning If you need more specific guidance for your particular self-hosted setup, I recommend reaching out to the community on [Ory Community Slack](http://slack.ory.sh/) as suggested in the documentation.cold-winter-87802
05/03/2025, 9:33 PMbrief-egg-61061
05/03/2025, 9:33 PMbrief-egg-61061
05/03/2025, 9:33 PM--response-type
flag. Based on the [Hydra CLI documentation](https://www.ory.sh/docs/hydra/cli/hydra-create-oauth2-client#options), the main response types are: ### 1. code
(Authorization Code) ory create oauth2-client --response-type code
Uses: - Most secure flow for server-side applications - Provides an authorization code that must be exchanged for tokens - Suitable for web applications with a backend server - Default response type if not specified ### 2. token
(Implicit Flow) ory create oauth2-client --response-type token
Uses: - Provides access tokens directly in the redirect URI fragment - Historically used for single-page applications (SPAs) - Less secure than the authorization code flow - Generally not recommended for new applications ### 3. id_token
(OpenID Connect) ory create oauth2-client --response-type id_token
Uses: - Returns an ID token containing user identity information - Used for authentication-only flows - Part of OpenID Connect specification ### 4. Combination Types You can also combine response types for more complex flows: ory create oauth2-client --response-type "code id_token"
Uses: - Hybrid flows that combine aspects of multiple flows - Provides both authorization code and ID token in a single response - Useful for applications that need immediate access to identity information while still using the more secure code flow ## Example Client Configurations As shown in the [Ory documentation](https://www.ory.sh/docs/hydra/guides/oauth2-clients#example-oauth2-clients), here's an example of creating a client with specific response types: ory create oauth2-client \ --grant-type authorization_code --grant-type refresh_token \ --response-type code \ --scope openid --scope offline_access \ --token-endpoint-auth-method client_secret_post \ --redirect-uri <https://my-app.com/callback>
This creates a client that uses the authorization code flow (code
response type) with refresh token support. When choosing response types, consider your application architecture, security requirements, and the specific OAuth2/OpenID Connect features you need to implement.cold-winter-87802
05/03/2025, 9:36 PMbrief-egg-61061
05/03/2025, 9:36 PMbrief-egg-61061
05/03/2025, 9:36 PMory_at_JGhESDjKfHMQ8Wcy0cC3.hIQxGmX37ydn8WmKAnlD3U
2. JWT Access Tokens: - Self-contained tokens with embedded claims - Can be validated without a database lookup - Faster validation (~1ms vs ~50ms for opaque tokens) - Revocation is eventual rather than immediate As noted in the [Ory documentation](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token), opaque tokens have an advantage for revocation, while JWTs have an advantage for performance. ## Recommendation for Web to API Calls For most web-to-API scenarios, opaque tokens are recommended because: 1. They provide immediate revocation capability 2. They don't expose potentially sensitive information in the token itself 3. They're the default in Ory for security reasons However, if your architecture requires high-performance API calls with minimal latency and you can tolerate eventual (rather than immediate) token revocation, JWT access tokens may be more suitable. ## Important Considerations 1. Token Usage: As mentioned in the [Ory FAQ](https://www.ory.sh/docs/hydra/faq#should-i-use-oauth2-tokens-for-authentication), OAuth2 tokens should be used for authorization to APIs, not for user authentication. For authentication, use OpenID Connect ID Tokens. 2. Alternative Approaches: For simple web applications that don't need third-party API access, you might not need OAuth2 at all. As noted in the [Ory documentation](https://www.ory.sh/docs/security-model#can-i-use-oauth-20--openid-connect), session cookies might be a simpler approach for your own web applications. 3. Configuration: If you decide to use JWT tokens, you can configure them either globally or per-client as shown in the [JWT access token documentation](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token#jwt-access-tokens). For most web-to-API scenarios where security is a priority, opaque tokens provide the best balance of security and functionality.cold-winter-87802
05/03/2025, 9:38 PMbrief-egg-61061
05/03/2025, 9:38 PMbrief-egg-61061
05/03/2025, 9:38 PMGET /sessions/token-exchange
This endpoint allows you to exchange session information between different authentication contexts, as documented in the [Ory API Reference](https://www.ory.sh/docs/reference/api). You'll need to provide the appropriate parameters (init_code
and return_to_code
) to complete the exchange. ## 2. Using the Sessions Whoami Endpoint The more common approach is to use the /sessions/whoami
endpoint, which can return the session token when called from a browser with an active session cookie: javascript import { Configuration, FrontendApi } from "@ory/client" const frontend = new FrontendApi( new Configuration({ basePath: "<https://your-project.projects.oryapis.com>", baseOptions: { withCredentials: true, // Important for sending cookies }, }), ) // Get session information and token const { data } = await frontend.toSession() const sessionToken = data.session_token
As described in the [Ory documentation](https://www.ory.sh/docs/identities/sign-in/check-session-token-cookie-api), the /sessions/whoami
endpoint checks for both cookies and tokens, making it versatile for different authentication scenarios. ## Important Notes 1. The session token has a format like MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj
and can be used in the Authorization
header for subsequent API calls. 2. For security reasons, Ory recommends using session cookies for browser-based applications and session tokens for non-browser applications, as mentioned in the [session management documentation](https://www.ory.sh/docs/kratos/session-management/overview). 3. If you're looking for OAuth2 tokens specifically (like access tokens or refresh tokens), you would need to use the OAuth2 token endpoint as described in the [API reference](https://www.ory.sh/docs/reference/api), but this is a different flow from session token exchange.cold-winter-87802
05/03/2025, 9:45 PMbrief-egg-61061
05/03/2025, 9:45 PMbrief-egg-61061
05/03/2025, 9:46 PMory_st_
) are used to maintain user authentication state and are primarily used for: 1. Native/Mobile Applications: When you can't use cookies, session tokens provide a way to maintain authentication state. javascript // Example of using a session token in a native app const session = await frontend.toSession({ xSessionToken: "your-session-token" })
2. Server-to-Server Communication: When your backend needs to make authenticated requests on behalf of a user. 3. Non-Browser Clients: As mentioned in the [Ory documentation](https://www.ory.sh/docs/identities/native-browser), "Native apps use the https://$<http://PROJECT_SLUG.projects.oryapis.com/self-service/{flow-type}/api|PROJECT_SLUG.projects.oryapis.com/self-service/{flow-type}/api>
endpoint... Ory issues an Ory Session Token instead of an Ory Session Cookie." ## Opaque Tokens (OAuth2 Access Tokens) Opaque tokens (prefixed with ory_at_
) are OAuth2 access tokens used for API authorization and are best suited for: 1. API Access Control: When you need to authorize access to protected resources/APIs. 2. Third-Party Integration: When your application needs to access third-party services on behalf of a user. 3. Microservices Architecture: When different services need to communicate with varying levels of access. 4. Web-to-API Calls: As you asked in a previous question, opaque tokens are generally recommended for web-to-API calls because they provide immediate revocation capability. ## Key Differences 1. Purpose: - Session tokens represent user authentication state - Opaque tokens represent authorization to access specific resources 2. Issuance: - Session tokens are issued after user authentication - Opaque tokens are issued through OAuth2 flows (authorization code, client credentials, etc.) 3. Security Model: - Session tokens typically have longer lifespans - Opaque tokens are designed for shorter-lived, specific authorizations 4. Revocation: - Both can be immediately revoked, unlike JWT tokens which have eventual revocation As noted in the [Ory security compliance documentation](https://www.ory.sh/docs/security-compliance/token-formats), these different token types have specific prefixes to make them easily identifiable for security purposes. In summary, use session tokens for maintaining authentication state across requests, especially in non-browser environments, and use opaque tokens when you need to authorize access to specific APIs or resources.cold-winter-87802
05/03/2025, 10:32 PMbrief-egg-61061
05/03/2025, 10:32 PMbrief-egg-61061
05/03/2025, 10:33 PMjavascript // Example permission check const isAllowed = await ory .permission .checkPermission({ namespace: "files", object: "document-123", relation: "view", subjectId: "user:john@example.com" }) .then(({data}) => data.allowed)
This approach allows you to define complex relationship-based permissions as described in the [Ory Permissions documentation](https://www.ory.sh/docs/guides/permissions/overview#checking-for-permissions). ### 2. Using OAuth2 Access Tokens with Scopes For API authorization using OAuth2: 1. Protect your API with access token validation 2. Check the token's scopes to determine permissions javascript // In your API middleware function checkScope(requiredScope) { return (req, res, next) => { // Validate the token and check if it has the required scope const token = req.headers.authorization?.split(' ')[1] // Introspect the token to verify it and get its scopes ory.introspectOAuth2Token({ token: token }).then(({data}) => { if (data.active && data.scope.includes(requiredScope)) { next() } else { res.status(403).json({ error: "Insufficient permissions" }) } }) } } // Usage app.get('/api/resource', checkScope('read:resource'), (req, res) => { // Handle the request })
### 3. Best Practices According to the [Ory documentation](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#best-practices), you should: 1. Don't cache permission checks: Ory provides a low-latency system with appropriate caching. Incorrect caching can lead to security vulnerabilities. 2. Perform permission checks in every critical path: Always check permissions before allowing access to protected resources. javascript // Before any sensitive operation const allowed = await ory.permission.checkPermission({...}) if (!allowed) { return res.status(403).send("Permission denied") }
3. Use fine-grained permissions: Define permissions at the appropriate level of granularity to enforce the principle of least privilege. 4. Audit permissions regularly: Review and audit permissions to ensure users only have the access they need. ### 4. Synchronous Authorization Flow As described in the [Ory documentation](https://www.ory.sh/docs/keto/guides/simple-access-check-guide#synchronous-authorization-flow): > It's crucial that all permission checks are performed using Ory Permissions. This means that before any action is taken, the required permission should be checked to ensure that the action is allowed. For a complete implementation, you should: 1. Authenticate the user (using Ory Identities or OAuth2) 2. Extract the subject identifier 3. Check permissions using Ory Permissions 4. Allow or deny access based on the permission check result This approach ensures consistent and secure access control across your application. Would you like me to elaborate on any specific aspect of API authorization with Ory?cold-winter-87802
05/03/2025, 11:04 PMbrief-egg-61061
05/03/2025, 11:04 PMbrief-egg-61061
05/03/2025, 11:04 PMyaml mutators: id_token: enabled: true config: issuer_url: <http://127.0.0.1:4455/> jwks_url: file:///etc/config/oathkeeper/id_token.jwks.json claims: | { "session": {{ .Extra | toJson }} }
This configuration allows Oathkeeper to take an Ory Session Cookie and transform it into a JWT that contains the session information. ## Common Token Exchange Scenarios 1. Cookie to JWT: Converting Ory Session Cookies to JWTs for API access Cookie: ory_kratos_session=... → Authorization: bearer eyJhbGci...
2. Session to Header: Converting session information to custom headers Cookie: ory_kratos_session=... → X-User-ID: user-123
3. OAuth2 Token to JWT: Transforming OAuth2 tokens to standardized JWTs As mentioned in the [Ory Proxy documentation](https://www.ory.sh/docs/guides/cli/proxy-and-tunnel#use-ory-proxy-with-json-web-tokens), Oathkeeper (and the deprecated Ory Proxy) can translate Ory credentials to JSON Web Tokens, making it easier to integrate with various backend services. ## Mutator Types for Token Exchange Oathkeeper offers several mutators for different token exchange needs: 1. ID Token Mutator: Converts authentication info to a JWT 2. Header Mutator: Adds authentication data to HTTP headers 3. Cookie Mutator: Sets cookies based on authentication data 4. NoOp Mutator: Passes authentication data unchanged For web-to-API scenarios, the ID Token Mutator is particularly useful as it allows browser applications using cookies to communicate with APIs expecting token-based authentication. Would you like more specific information about implementing a particular token exchange pattern with Oathkeeper?big-night-72627
06/04/2025, 5:39 AMbrief-egg-61061
06/04/2025, 5:39 AMbrief-egg-61061
06/04/2025, 5:39 AMjson [ { "id": "public", "version": "v0.36.0-beta.4", "upstream": { "url": "$RESOURCE_SERVER", "preserve_host": true }, "match": { "url": "<https?://[^/]+/creditors/(\\.docs(/.*)?|.*[./-]public)$>", "methods": [ "GET", "HEAD" ] }, "authenticators": [ { "handler": "noop" } ], "authorizer": { "handler": "allow" }, "mutators": [ { "handler": "noop" } ] } ]
This rule allows public (unauthenticated) access to certain endpoints and forwards requests to the upstream service. ### Running Oathkeeper in Kubernetes - Pod Specification: The Oathkeeper container is started with a memory limit (e.g., 200Mb). - Config and Rules: Configuration and rules are mounted into the container, typically via ConfigMaps or Secrets. - Liveness Probes: Kubernetes liveness probes can be configured to hit a public rule endpoint to check health. Note: The user observed that Oathkeeper’s memory usage could vary on startup, and setting a memory limit in the pod spec helped manage resource usage. The pod would restart if Oathkeeper exceeded the limit, but would eventually start successfully and function as expected. > “When I set a 200Mb memory limit for the pod, the pod never consumes more than 200Mb (it is around 120Mb in my case: 50Mb for Oathkeeper and 70Mb for my other processes). However, I get Back-off restarting failed container web-server in pod XXXXX
Kubernetes events when re-creating the pod with the same frequency (about 50%).” > — [GitHub Issue: 600Mb of memory consumed on the start](https://github.com/ory/oathkeeper/issues/1230) ### Summary - Deploy Oathkeeper as a container in your Kubernetes pod. - Mount your configuration and access rules. - Use Kubernetes probes for health checks. - Set resource limits as needed. This is a basic example, but it reflects a real-world Oathkeeper + Kubernetes deployment as described in the knowledge sources. For more advanced setups, you would typically use ConfigMaps, Secrets, and possibly sidecar containers for upstream services or logging. If you need a full Kubernetes manifest or more details, let me know!big-night-72627
06/04/2025, 5:40 AMbrief-egg-61061
06/04/2025, 5:40 AMbrief-egg-61061
06/04/2025, 5:40 AMbig-night-72627
06/04/2025, 5:43 AM❯ k describe ingress internal -n auth-c0
Name: internal
Labels: <http://app.kubernetes.io/component=api|app.kubernetes.io/component=api>
<http://app.kubernetes.io/instance=auth-c0-vwg-stg-1-ew1|app.kubernetes.io/instance=auth-c0-vwg-stg-1-ew1>
<http://app.kubernetes.io/managed-by=Helm|app.kubernetes.io/managed-by=Helm>
<http://app.kubernetes.io/name=auth|app.kubernetes.io/name=auth>
<http://app.kubernetes.io/version=main-bcd7ffac|app.kubernetes.io/version=main-bcd7ffac>
cell=cell0
<http://helm.sh/chart=auth-1.1.0-bcd7ffac|helm.sh/chart=auth-1.1.0-bcd7ffac>
Namespace: auth-c0
Address: <http://ingress-internal-b9753b17e64918a9.elb.eu-west-1.amazonaws.com|ingress-internal-b9753b17e64918a9.elb.eu-west-1.amazonaws.com>
Ingress Class: nginx-internal
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
<http://stg-1.apps.vwg.rivianvw.io|stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://web.stg-1.apps.vwg.rivianvw.io|web.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://docs.stg-1.apps.vwg.rivianvw.io|docs.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://lineside.stg-1.apps.vwg.rivianvw.io|lineside.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://lineside-admin.stg-1.apps.vwg.rivianvw.io|lineside-admin.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://ride.stg-1.apps.vwg.rivianvw.io|ride.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://elemental.stg-1.apps.vwg.rivianvw.io|elemental.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://login.stg-1.apps.vwg.rivianvw.io|login.stg-1.apps.vwg.rivianvw.io>
/ login-svc:80 (100.64.113.116:80)
<http://kratos.stg-1.apps.vwg.rivianvw.io|kratos.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://frame.stg-1.apps.vwg.rivianvw.io|frame.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://middleware.stg-1.apps.vwg.rivianvw.io|middleware.stg-1.apps.vwg.rivianvw.io>
/ oathkeeper-svc:4455 (100.64.113.122:4455)
<http://keto.stg-1.apps.vwg.rivianvw.io|keto.stg-1.apps.vwg.rivianvw.io>
/ keto-svc:4466 (100.64.115.0:4466)
<http://hydra.stg-1.apps.vwg.rivianvw.io|hydra.stg-1.apps.vwg.rivianvw.io>
/ hydra-svc:4444 (100.64.115.2:4444)
Annotations: <http://argocd.argoproj.io/tracking-id|argocd.argoproj.io/tracking-id>: auth-c0-vwg-stg-1-ew1:<http://networking.k8s.io/Ingress:auth-c0/internal|networking.k8s.io/Ingress:auth-c0/internal>
Events: <none>
brief-egg-61061
06/04/2025, 5:43 AMbrief-egg-61061
06/04/2025, 5:43 AMkubectl describe ingress
output and the available knowledge sources, your setup appears to follow a common pattern for deploying Ory Oathkeeper in Kubernetes: - Oathkeeper is deployed as a service (oathkeeper-svc:4455
) and is set as the backend for most of your application hostnames. This means all incoming requests to those hosts will be routed through Oathkeeper, which can then enforce your access rules and authentication/authorization logic before proxying to your upstream services. - Specialized services like login-svc
, keto-svc
, and hydra-svc
are routed directly, which is a typical approach if you want to expose those services separately or if they have their own access control mechanisms. This matches the general deployment pattern described in the knowledge sources, where Oathkeeper acts as a reverse proxy in front of your services, and is deployed as a container (or service) in your Kubernetes cluster. The Ingress is configured to forward requests to Oathkeeper, which then applies its access rules and proxies allowed requests to the appropriate upstream service within your cluster. Configuration and access rules are typically mounted via ConfigMaps or Secrets, and health checks can be configured using Kubernetes probes[GitHub Issue: 600Mb of memory consumed on the start](https://github.com/ory/oathkeeper/issues/1230). What to check: - Ensure your Oathkeeper access rules are correctly configured to match the routes and methods you want to allow or deny. - Make sure your Oathkeeper service is healthy and reachable at the specified port. - If you want to protect the login, hydra, or keto endpoints with Oathkeeper as well, you would need to route those through Oathkeeper instead of directly to their services. Summary: Your setup looks correct for routing most application traffic through Oathkeeper using Kubernetes Ingress. This is a standard and recommended approach for enforcing centralized access control in a Kubernetes environment with Ory Oathkeeper. If you have specific issues or want to protect additional endpoints, you may need to adjust your Ingress or Oathkeeper rules accordingly.big-night-72627
06/04/2025, 7:26 PMmethods:
oidc:
config:
providers:
#- id: "RVW Tech"
- id: microsoft
Shows up as microsoft
on the login display buttonbrief-egg-61061
06/04/2025, 7:26 PMbrief-egg-61061
06/04/2025, 7:26 PMlabel
field in your OIDC provider configuration. The id
field is used internally, while the label
field controls what is shown to users on the login button. For example: yaml methods: oidc: config: providers: - id: microsoft label: "Sign in with RVW Tech" client_id: "..." client_secret: "..." provider: microsoft
With this configuration, the login button will display "Sign in with RVW Tech" instead of "microsoft" or the provider ID. This is supported in both the Ory Console and CLI as described in the [Ory social sign-in documentation](https://www.ory.sh/docs/identities/get-started/social-sign-in#add-a-social-provider). If you do not set the label
, the button will default to showing the provider's id
(e.g., "microsoft") as you observed.big-night-72627
06/20/2025, 4:28 PMbrief-egg-61061
06/20/2025, 4:28 PMbrief-egg-61061
06/20/2025, 4:28 PMbig-night-72627
06/20/2025, 4:35 PMbrief-egg-61061
06/20/2025, 4:35 PMbrief-egg-61061
06/20/2025, 4:35 PM<http://localhost:4433>
for the public API). 2. Obtain a session cookie by performing a login or registration flow using the browser or programmatically. When a user successfully logs in or registers via the browser-based flow, Kratos issues a session cookie (typically named ory_kratos_session
) [Session management overview](https://www.ory.sh/docs/kratos/session-management/overview#using-ory-session-cookie). 3. Extract the session cookie from your browser's developer tools (Application > Cookies) after a successful login or registration. 4. Attach the session cookie to your local service's HTTP requests to Kratos. For example, in Go or Node.js, you can set the Cookie
header: go // Go example using the Kratos Go SDK session, _, err := apiClient.FrontendApi.ToSession(context.Background()). Cookie("ory_kratos_session=<your-session-cookie-here>").Execute()
Or with cURL: sh curl '<http://localhost:4433/sessions/whoami>' \ -H 'Cookie: ory_kratos_session=<your-session-cookie-here>'
This allows your local service to authenticate as the user associated with that session cookie [Self-hosted Go example](https://www.ory.sh/docs/kratos/self-hosted/go) | [Session management overview](https://www.ory.sh/docs/kratos/session-management/overview#using-ory-session-cookie). Note: - The session cookie is only valid for the domain and path it was issued for. For local development, ensure your service and Kratos are both using localhost
(not 127.0.0.1
), as cookies are domain-specific [Local development](https://www.ory.sh/docs/getting-started/local-development#local-development). - If you need to automate login for testing, you can use the Kratos SDK to programmatically perform the login flow and extract the session cookie from the response. This approach allows your local service to make authenticated calls to your self-hosted Kratos instance using a valid session cookie.big-night-72627
06/24/2025, 10:22 PMbrief-egg-61061
06/24/2025, 10:22 PMbrief-egg-61061
06/24/2025, 10:22 PMremote
authorizer, which allows you to implement custom authorization logic. ## Using the remote
Authorizer for Email Whitelisting The remote
authorizer in Oathkeeper makes an HTTP request to a service you control, which can implement your email whitelist logic. Here's how to set it up: json { "id": "some-id", "upstream": { "url": "<http://my-backend-service>" }, "match": { "url": "<http://my-app/api/<.*>>", "methods": ["GET"] }, "authenticators": [ { "handler": "bearer_token" } ], "authorizer": { "handler": "remote", "config": { "remote": "<http://my-email-authorizer/authorize>", "headers": { "X-Subject": "{{ print .Subject }}", "X-Email": "{{ print .Extra.email }}" } } }, "mutators": [ { "handler": "noop" } ] }
In this configuration: 1. The remote
authorizer sends a request to your custom authorization service 2. It passes the user's email address in the X-Email
header (assuming it's available in the .Extra.email
field) 3. Your service checks if the email is in the whitelist and returns: - HTTP 200 OK if the email is allowed - HTTP 403 Forbidden if the email is not allowed [Oathkeeper Pipeline Authorizers](https://www.ory.sh/docs/oathkeeper/pipeline/authz#remote) explains that the remote authorizer makes a HTTP POST request to your endpoint, and access is allowed only if your service returns a "200 OK" response. ## Implementing the Email Whitelist Service Your email whitelist service could be a simple HTTP server that: 1. Receives the request from Oathkeeper 2. Extracts the email from the headers 3. Checks if the email is in your whitelist 4. Returns the appropriate status code This approach gives you complete flexibility to implement any authorization logic you need, including email whitelisting, role-based access control, or more complex rules. Would you like more details on implementing the email whitelist service or configuring Oathkeeper for this use case?big-night-72627
06/24/2025, 10:23 PM"authorizers": [
{
"handler": "allow",
"config": {
"subject_matches": [
"<mailto:user1@example.com|user1@example.com>",
"<mailto:admin@example.com|admin@example.com>",
"<mailto:another.user@goriv.co|another.user@goriv.co>"
]
}
}
],
brief-egg-61061
06/24/2025, 10:23 PMbrief-egg-61061
06/24/2025, 10:24 PMallow
authorizer that supports a subject_matches
field with a list of email addresses as shown in your example. The allow
authorizer in Oathkeeper is a simple authorizer that permits every action without any configuration options. As described in the [Oathkeeper Pipeline Authorizers documentation](https://www.ory.sh/docs/oathkeeper/pipeline/authz): > This authorizer permits every action allowed. > > #### allow
configuration > > This handler isn't configurable. For email address whitelisting, you have a few alternative approaches: 1. Use the remote_json
authorizer: This allows you to send a JSON payload to a remote service that can check if the subject (email) is in your whitelist. 2. Use the remote
authorizer: Similar to remote_json
, but it forwards the original request body. 3. Create a custom authorizer: If you're running a self-hosted instance, you could potentially develop a custom authorizer plugin. If you want to implement a static whitelist without an external service, you might need to use Ory Keto (Permissions) with the keto_engine_acp_ory
authorizer, where you can define policies that include specific subjects. For a simpler approach, you could create a small microservice that implements the whitelist logic and use the remote
authorizer to call it, as I suggested in our previous conversation. Would you like me to provide more details on implementing one of these alternative approaches for your email whitelist?big-night-72627
06/27/2025, 4:29 AMbrief-egg-61061
06/27/2025, 4:29 AMbrief-egg-61061
06/27/2025, 4:29 AMid_token
mutator. This is a common pattern for enabling APIs or services that require JWTs for authentication, even if the original authentication was performed using a session cookie (such as the one issued by Ory Kratos). How it works: 1. Authenticate the session cookie: Use the cookie_session
authenticator in your Oathkeeper access rule to validate the Ory Kratos session cookie. 2. Mutate to an ID token: Use the id_token
mutator in your access rule. This mutator takes the authentication information (such as the subject and extra session data) and transforms it into a signed JWT (ID token). The JWT is then added to the Authorization: Bearer <jwt>
header of the proxied request. Example Oathkeeper Access Rule: json { "id": "example-rule", "upstream": { "url": "<http://my-backend-service>" }, "match": { "url": "<http://my-app/api/.*>", "methods": ["GET", "POST"] }, "authenticators": [ { "handler": "cookie_session", "config": { "check_session_url": "<http://kratos:4433/sessions/whoami>", "only": ["ory_kratos_session"] } } ], "authorizer": { "handler": "allow" }, "mutators": [ { "handler": "id_token", "config": { "issuer_url": "<http://oathkeeper:4455/>", "jwks_url": "file:///etc/config/oathkeeper/id_token.jwks.json", "claims": "{\"session\": {{ .Extra | toJson }}}" } } ] }
- The cookie_session
authenticator validates the session cookie. - The id_token
mutator issues a JWT containing session data. - The JWT is signed using the key specified in jwks_url
and is available to your backend via the Authorization
header. References: - [Oathkeeper Mutators: id_token](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token) - [Zero Trust with Access Proxy guide](https://www.ory.sh/docs/kratos/guides/zero-trust-iap-proxy-identity-access-proxy#id-token-mutator) - [Oathkeeper Mutator id_token configuration](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token-configuration) This setup allows you to seamlessly convert a session cookie to an ID token (JWT) for downstream services that require JWT-based authentication.big-night-72627
06/27/2025, 4:34 AMbrief-egg-61061
06/27/2025, 4:34 AMbrief-egg-61061
06/27/2025, 4:34 AMbrief-egg-61061
06/27/2025, 4:35 AMbrief-egg-61061
06/27/2025, 4:35 AMbig-night-72627
06/27/2025, 2:28 PMbrief-egg-61061
06/27/2025, 2:28 PMbrief-egg-61061
06/27/2025, 2:29 PMerror executing headers template "Bearer {{ print .IDToken }}" in rule "rvtech:frame:protected": template: rvtech:frame:protected:Authorization:1:16: executing "rvtech:frame:protected:Authorization" at <.IDToken>: can't evaluate field IDToken in type *authn.AuthenticationSession
What this means: - The header mutator is trying to use the template variable {{ print .IDToken }}
. - However, .IDToken
does not exist in the AuthenticationSession
struct that is passed to the mutator. As a result, the template engine cannot evaluate this field, causing the mutation handler to fail and the request to be denied with a 500 Internal Server Error. Why this happens: - In Oathkeeper, the available fields for templating in mutators are those present in the AuthenticationSession
struct, such as .Subject
, .Extra
, .Header
, and .MatchContext
([Session variables documentation](https://www.ory.sh/docs/oathkeeper/pipeline#session)). - .IDToken
is not a standard field in this struct. If you want to inject an ID token into a header, you must first use the id_token
mutator, which will generate the token and make it available for further mutation or as a header. How to fix: - If you want to add a Bearer token to the Authorization
header, use the id_token
mutator, which will generate a JWT and set it as the Authorization
header automatically. - Alternatively, if you want to use a header mutator, you need to reference fields that actually exist in the session, such as {{ print .Subject }}
or {{ print .Extra.some_field }}
. Summary: The error is due to referencing a non-existent .IDToken
field in your header mutator template. Update your rule to use valid fields from the AuthenticationSession
struct, or use the id_token
mutator if you want to generate and inject a JWT ([Oathkeeper Mutators documentation](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token), [Session variables](https://www.ory.sh/docs/oathkeeper/pipeline#session)).big-night-72627
06/27/2025, 2:34 PMbrief-egg-61061
06/27/2025, 2:34 PMbrief-egg-61061
06/27/2025, 2:34 PMAuthorization
header. However, the issue is with the use of {{ print .IDToken }}
in your header mutator. Problem: The field .IDToken
does not exist in the AuthenticationSession
struct that Oathkeeper exposes to mutators. This is confirmed by the error in your logs: can't evaluate field IDToken in type *authn.AuthenticationSession
How the id_token
mutator works: The id_token
mutator generates a JWT and, by default, sets it as the Authorization: Bearer <jwt>
header for the upstream request. You do not need to use a header mutator to set the Authorization
header with the ID token; the id_token
mutator does this automatically ([Oathkeeper Mutators: id_token](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token)). What you should do: - Remove the Authorization
header from your header mutator. - If you want to add custom headers (like X-User-Subject
), you can keep those in the header mutator. Corrected configuration: json { "id": "rvtech:frame:protected", "upstream": { "url": "<http://frame-svc>" }, "match": { "url": "<http|https>://frame.stg-1.apps.vwg.rivianvw.io/<(.*)?$>", "methods": ["GET", "POST", "OPTIONS"] }, "authenticators": [ { "handler": "cookie_session", "config": { "check_session_url": "<http://kratos-public:4433/sessions/whoami>", "only": ["ory_kratos_session"] } } ], "authorizer": { "handler": "allow" }, "mutators": [ { "handler": "id_token", "config": { "issuer_url": "<https://oathkeeper-proxy.stg-1.apps.vwg.rivianvw.io>", "jwks_url": "file:///etc/secrets/mutator.id_token.jwks.json", "claims": "{\"session\": {{ .Extra | toJson }}}" } }, { "handler": "header", "config": { "headers": { "X-User-Subject": "{{ print .Subject }}" } } } ] }
Summary: - The id_token
mutator will generate and inject the JWT as the Authorization: Bearer <jwt>
header. - Do not reference .IDToken
in the header mutator, as it does not exist. - You can use the header mutator for other custom headers, such as X-User-Subject
. This approach matches the documented and supported usage in the Ory Oathkeeper knowledge sources and will resolve the error you are seeing in your logs.big-night-72627
06/27/2025, 5:23 PMaccess_rules:
repositories:
- file:///etc/rules/access-rules.json
authenticators:
anonymous:
config:
subject: guest
enabled: true
cookie_session:
config:
check_session_url: <http://kratos-public:4433/sessions/whoami>
extra_from: '@this'
preserve_path: true
preserve_query: true
subject_from: identity.id
enabled: true
noop:
enabled: true
authorizers:
allow:
enabled: true
deny:
enabled: true
keto_engine_acp_ory:
config:
base_url: <https://keto-instance.com>
required_action: ""
required_resource: ""
enabled: false
errors:
fallback:
- json
handlers:
json:
config:
verbose: false
enabled: true
redirect:
config:
to: <https://kratos.stg-1.apps.vwg.rivianvw.io/self-service/login/browser?return_to=%s>
when:
- error:
- unauthorized
- forbidden
request:
header:
accept:
- text/html
enabled: true
log:
format: json
leak_sensitive_values: true
level: debug
redaction_text: ""
mutators:
header:
config:
headers:
X-User-Subject: '{{ print .Subject }}'
enabled: true
id_token:
config:
claims: |
{
"sub": "{{ .Subject }}",
"email": "{{ .Extra.identity.traits.email }}",
"name": {
"first": "{{ .Extra.identity.traits.name.first }}",
"last": "{{ .Extra.identity.traits.name.last }}"
}
}
issuer_url: <https://oathkeeper-proxy.stg-1.apps.vwg.rivianvw.io>
jwks_url: file:///etc/secrets/mutator.id_token.jwks.json
ttl: 15m
enabled: true
noop:
enabled: true
serve:
api:
port: 4456
prometheus:
port: 9000
proxy:
port: 4455
~ $
{
"id": "rvtech:frame:protected",
"upstream": {
"url": "<http://frame-svc>"
},
"match": {
"url": "<http|https>://frame.stg-1.apps.vwg.rivianvw.io/<(.*)?$>",
"methods": ["GET", "POST", "OPTIONS"]
},
"authenticators": [
{
"handler": "cookie_session",
"config": {
"check_session_url": "<http://kratos-public:4433/sessions/whoami>",
"only": ["ory_kratos_session"]
}
}
],
"authorizer": { "handler": "allow" },
"mutators": [
{
"handler": "id_token",
"config": {
"issuer_url": "<https://oathkeeper-proxy.stg-1.apps.vwg.rivianvw.io>",
"jwks_url": "file:///etc/secrets/mutator.id_token.jwks.json",
"claims": "{\"session\": {{ .Extra | toJson }}}"
}
},
{
"handler": "header",
"config": {
"headers": {
"X-User-Subject": "{{ print .Subject }}",
}
}
}
]
}
brief-egg-61061
06/27/2025, 5:23 PMbrief-egg-61061
06/27/2025, 5:24 PMid_token
mutator in Ory Oathkeeper is responsible for generating a JWT and, by default, setting it as the Authorization: Bearer <jwt>
header for the proxied request. You do not need to use the header mutator to set the Authorization
header with the ID token; the id_token
mutator does this automatically. The header mutator is only needed if you want to add additional custom headers (like X-User-Subject
) [Oathkeeper Mutators: id_token](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token). Why you might not see the Bearer token: - If the id_token
mutator is present and enabled in your rule, and the mutator is correctly configured, Oathkeeper will inject the JWT as the Authorization
header. - If you are not seeing the Authorization: Bearer <jwt>
header, possible reasons include: - The id_token
mutator is not being executed (e.g., due to rule matching issues or an error in the mutator). - There is an error in the mutator configuration or in the JWKS file. - The upstream service or a proxy in front of Oathkeeper is stripping or not forwarding the Authorization
header. What to check: 1. Logs: Check Oathkeeper logs for any errors related to the id_token
mutator. Errors in the mutator will prevent the header from being set. 2. Rule Matching: Ensure the request matches the rule that includes the id_token
mutator. 3. JWKS File: Make sure the JWKS file is present and readable at the specified path. 4. Header Forwarding: Ensure there is no proxy or middleware between Oathkeeper and your upstream service that strips the Authorization
header. Summary: With your current configuration, the id_token
mutator should automatically set the Authorization: Bearer <jwt>
header. You do not need to set this header manually in the header mutator. If the header is missing, check for errors in the mutator, rule matching, or header forwarding as described above [Oathkeeper Mutators: id_token](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token).big-night-72627
07/01/2025, 9:21 PMbrief-egg-61061
07/01/2025, 9:21 PMbrief-egg-61061
07/01/2025, 9:21 PMOPTIONS
preflight request for CORS) does not match any of the access rules you have configured in Oathkeeper. Key points from the logs: - The request is an OPTIONS
request to /api/oauth-clients
on the host <http://middleware.stg-1.apps.vwg.rivianvw.io|middleware.stg-1.apps.vwg.rivianvw.io>
. - Oathkeeper could not find a rule that matches this combination of host, path, and method, so it returns a 404. Why this causes a CORS error: - Browsers send an OPTIONS
preflight request before certain cross-origin requests (like POST
with custom headers). - If Oathkeeper does not have a rule that matches the OPTIONS
request, it will return a 404, and the browser will block the actual request due to failed CORS negotiation. How to fix: - Ensure you have an Oathkeeper rule that matches the OPTIONS
method for the path and host in question. - Your rule should include "methods": ["OPTIONS", "POST", ...]
and match the path /api/oauth-clients
for the host <http://middleware.stg-1.apps.vwg.rivianvw.io|middleware.stg-1.apps.vwg.rivianvw.io>
. Summary: Oathkeeper is denying the CORS preflight request because there is no matching rule for the OPTIONS
method on the requested path and host. Add or update your access rule to explicitly allow the OPTIONS
method for this endpoint to resolve the issue. This behavior is consistent with how Oathkeeper matches requests to rules, as seen in the error and stack trace in your logs.big-night-72627
07/01/2025, 10:45 PMbrief-egg-61061
07/01/2025, 10:45 PMbrief-egg-61061
07/01/2025, 10:45 PMOPTIONS
verbs (such as those used in CORS preflight requests) in Ory Oathkeeper, you must ensure that your access rules explicitly include the OPTIONS
method in the methods
array for the relevant paths and hosts. If an incoming OPTIONS
request does not match any rule, Oathkeeper will return a 404 error, which will cause CORS preflight requests to fail and block the actual request from the browser. For example, your rule should look like this: json { "id": "example-rule", "upstream": { "url": "<http://your-backend-service>" }, "match": { "url": "<http://your-host/api/your-endpoint>", "methods": ["GET", "POST", "OPTIONS"] }, "authenticators": [ { "handler": "noop" } ], "authorizer": { "handler": "allow" }, "mutators": [ { "handler": "noop" } ] }
By including "OPTIONS"
in the methods
array, Oathkeeper will match and process preflight requests, allowing CORS to function correctly. This is necessary because, as shown in your logs, a missing rule for the OPTIONS
method results in a "Requested url does not match any rules" error and a 404 response, which breaks CORS negotiation. This approach is confirmed by the error and behavior described in your logs and is consistent with Oathkeeper's rule matching logic [Oathkeeper API access rules](https://www.ory.sh/docs/oathkeeper/api-access-rules#access-rule-format).cold-winter-87802
07/02/2025, 12:20 AMbrief-egg-61061
07/02/2025, 12:21 AMbrief-egg-61061
07/02/2025, 12:21 AMcookie_session
authenticator in your Oathkeeper rule, but the incoming request is an OPTIONS
preflight (which does not include cookies), Oathkeeper will attempt to authenticate the request using the cookie_session
authenticator. Since there is no cookie present, the authenticator will not be able to validate the session, and the request may be denied unless you have configured a fallback authenticator (such as anonymous
or noop
) in your rule. The knowledge sources confirm that Oathkeeper processes authenticators in order, and if the first authenticator cannot handle the request (e.g., due to missing cookies), it will pass the request to the next authenticator in the list. If none can handle the request, access is denied [Oathkeeper Authenticators](https://www.ory.sh/docs/oathkeeper/pipeline/authn). To handle CORS preflight (OPTIONS
) requests when using `cookie_session`: - Add a fallback authenticator like anonymous
or noop
after cookie_session
in your rule's authenticators
array. This allows unauthenticated OPTIONS
requests to be accepted, while still requiring authentication for other methods. Example: json "authenticators": [ { "handler": "cookie_session", "config": { "check_session_url": "<http://kratos-public:4433/sessions/whoami>", "only": ["ory_kratos_session"] } }, { "handler": "anonymous" } ]
This setup ensures that authenticated requests use the session cookie, but unauthenticated OPTIONS
requests (which lack cookies) are still allowed through, enabling proper CORS handling [Oathkeeper Authenticators](https://www.ory.sh/docs/oathkeeper/pipeline/authn). If you do not add a fallback authenticator, Oathkeeper will deny the OPTIONS
request, resulting in CORS errors, as seen in your logs.big-night-72627
07/02/2025, 1:51 AMbrief-egg-61061
07/02/2025, 1:51 AMbrief-egg-61061
07/02/2025, 1:51 AMerror executing claims template in rule "rvtech:middleware:protected": template: ... at <.Extra.identity.traits.email>: nil pointer evaluating interface {}.traits
What’s happening: When an OPTIONS
(CORS preflight) request is received, Oathkeeper is attempting to execute your id_token
mutator’s claims template, which references .Extra.identity.traits.email
. However, for unauthenticated or anonymous requests (like most preflight OPTIONS
), there is no session, so .Extra.identity
is nil
. Attempting to access .traits
on a nil
value causes this error. Why this happens: - The id_token
mutator is always run, even for unauthenticated requests (such as those handled by the anonymous
authenticator). - The claims template expects session data that does not exist for anonymous or preflight requests. How to fix: You need to make your claims template resilient to missing data. The knowledge sources do not provide a direct example of conditional logic in claims templates, but the error and the Oathkeeper mutator documentation make it clear that you must avoid referencing fields that may not exist for all request types ([Oathkeeper Mutators: id_token](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token)). Recommended approach: - Use the anonymous
authenticator as a fallback for OPTIONS
requests (which you are already doing). - Adjust your claims template to handle missing fields. For example, you can use the default
function in Go templates to provide a fallback value: json "claims": "{ \"sub\": \"{{ .Subject }}\", \"email\": \"{{ .Extra.identity.traits.email | default \"\" }}\" }"
This way, if .Extra.identity.traits.email
is missing, it will default to an empty string, preventing the nil pointer error. Summary: Your error occurs because the claims template in your id_token
mutator does not handle missing session data, which is common for unauthenticated requests like CORS preflights. Update your claims template to use default values for fields that may not exist to resolve this issue.