Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const VERIFY_CATEGORIES = [
'Getting Started',
'eIDs',
'Guides & Tools',
'Extensions',
'Integrations',
'Reference',
];
Expand Down
59 changes: 59 additions & 0 deletions src/components/TabGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useState } from 'react';
import cx from 'classnames';

export type TabListProps = {
children: React.ReactNode;
};

export type ControlledTabGroupProps = {
tabs: {
header: React.ReactNode;
body: () => React.ReactNode;
}[];
};

export function TabGroup(props: TabListProps) {
return (
<nav className="flex flex-row items-center gap-2 m-0 overflow-x-auto mb-4">
{props.children}
</nav>
);
}

TabGroup.NavItem = function (
props: { children: React.ReactNode, active: boolean } & React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>,
) {
return (
<button
{...props}
className={cx(
'text-md py-3 flex flex-row items-center justify-center gap-2 border-b-2 hover:text-primary-600',
{
"font-medium text-primary-600 border-b-primary-600": props.active
}
)}
>
{props.children}
</button>
);
};

export function ControlledTabGroup(props: ControlledTabGroupProps) {
const [activeTabIndex, setActiveTabIndex] = useState(0);

const Body = props.tabs[activeTabIndex].body();

return (
<div>
<TabGroup>
{Object.values(props.tabs).map((tab, i) => (
<TabGroup.NavItem active={i === activeTabIndex} onClick={() => setActiveTabIndex(i)}>{tab.header}</TabGroup.NavItem>
))}
</TabGroup>
{Body}
</div>
);
}
45 changes: 45 additions & 0 deletions src/pages/verify/extensions/ciam-interop.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
product: verify
category: Extensions
sort: 1
title: CIAM Interop
subtitle: Provides Interoperability with CIAM systems, such as Microsoft Entra External ID.
---

The CIAM Interop extension provides interoperability with CIAM systems past what Idura Verify supports out-of-the-box.

# Synthesized Email Claims

Some CIAM systems require that OIDC providers always provide an `email` claim. Usually, [eID providers](../../e-ids/) do not provide emails. In order to enable the use of Idura Verify as a provider for these systems, the `dummy_email_domain` option can be used to synthesize the `email` claim where it does not exist.

If set, the extension will inspect the token claims and if no `email` is present in the input, add one with the value `{user.sub}@{dummy_email_domain}`. The user sub is a unique ID per user which is consistent between multiple logins on the same eID.

# Verified Emails

While some systems only require that the `email` is set, some also require the `email_verified` claim to be `true`. If the `force_email_verified` flag is enabled, the extension will set the `email_verified` claim in addition to the `email` claim.

**Dummy Email Domains must be enabled**. The extension will not set the `email_verified` claim, if the `email` claim is already present in the input JWT.

# Unnesting Nested Claims

Some CIAM providers do not support nested (object) claims in JWTs. The `unnesting_namespace` option can be used to flatten the incoming JWT.

If a `unnesting_namespace` is set, all nested claims (`a.b.c`) will be converted to namespaced claims (`{unnesting_namespace}/a/b/c`). Note: The unnesting namespace **must** begin with `https://`

For example, the OpenID standard address claim:

```json
{
"address": {
"street_address": "value"
}
}
```

will be flattened to:

```json
{
"{unnesting_namespace}/address/street_address": "value"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://criipto.com/extensions/webhook/post-auth-request.1.0.schema.json",
"title": "PostAuthWebhookExtension10Request",
"type": "object",
"required": ["method", "headers", "body"],
"additionalProperties": false,
"properties": {
"method": {
"type": "string",
"enum": ["POST"]
},
"headers": {
"type": "object",
"additionalProperties": false,
"required": ["Authorization", "Content-Type"],
"properties": {
"Authorization": { "type": "string" },
"Content-Type": { "type": "string", "enum": ["application/json"] }
}
},
"body": {
"type": "object",
"additionalProperties": false,
"required": ["event", "conversationId", "environment", "user", "resumeUrl"],
"properties": {
"event": {
"type": "string",
"enum": ["post-auth-event-1.0"]
},
"conversationId": {
"type": "string",
"description": "Random ID identifying this particular login session. Should be used as a session key to persist state between post-auth and post-auth-resume."
},
"environment": {
"type": "string",
"enum": ["test", "production"]
},
"user": {
"type": "object",
"additionalProperties": true,
"required": ["sub"],
"properties": {
"sub": { "type": "string" }
}
},
"resumeUrl": {
"type": "string",
"description": "If the webhook responds with a redirect, the login process is effectively paused until the user is directed back to this URL, at which point the webhook will receive a post-auth-resume event."
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://criipto.com/extensions/webhook/post-auth-response.1.0.schema.json",
"title": "PostAuthWebhookExtension10Response",
"anyOf": [
{
"type": "object",
"required": ["status", "body"],
"additionalProperties": false,
"properties": {
"status": {
"type": "integer",
"enum": [200]
},
"body": {
"$ref": "#/$defs/okBody"
}
}
},
{
"type": "object",
"required": ["status"],
"additionalProperties": false,
"properties": {
"status": {
"type": "integer",
"enum": [204]
}
}
},
{
"type": "object",
"required": ["status", "headers"],
"additionalProperties": false,
"properties": {
"status": {
"type": "integer",
"enum": [303]
},
"headers": {
"type": "object",
"additionalProperties": { "type": "string" },
"required": ["Location"],
"properties": {
"Location": { "type": "string" }
}
}
}
},
{
"type": "object",
"required": ["status", "body"],
"additionalProperties": false,
"properties": {
"status": {
"type": "integer",
"enum": [403]
},
"body": {
"type": "object",
"required": ["error"],
"additionalProperties": false,
"properties": {
"error": {
"type": "string"
},
"error_description": {
"type": "string"
},
"error_uri": {
"type": "string"
}
}
}
}
},
{
"type": "object",
"required": ["status", "body"],
"additionalProperties": false,
"properties": {
"status": {
"type": "integer",
"enum": [400, 401, 402, 403, 500, 501, 502, 503, 504]
},
"body": {
"$ref": "#/$defs/errorBody"
}
}
}
],
"$defs": {
"claimsOperations": {
"type": "object",
"additionalProperties": false,
"properties": {
"$set": {
"type": "object",
"description": "Map of claims to be set on the resulting token."
},
"$remove": {
"type": "array",
"description": "Claim keys which should be removed from the token.",
"items": {
"type": "string"
}
}
}
},
"okBody": {
"anyOf": [
{
"type": "object",
"required": ["claims"],
"additionalProperties": false,
"deprecated": true,
"description": "Deprecated: Prefer using 'claimsOperations.$set'",
"properties": {
"claims": {
"type": "object",
"description": "Claims to be set on the resulting token. Equivalent to 'claimsOperations.$set'"
}
}
},
{
"type": "object",
"required": ["claimsOperations"],
"additionalProperties": false,
"properties": {
"claimsOperations": {
"$ref": "#/$defs/claimsOperations"
}
}
}
]
},
"errorBody": {
"type": "object",
"required": ["message"],
"additionalProperties": false,
"properties": {
"message": { "type": "string" }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://criipto.com/extensions/webhook/post-auth-resume-request.1.0.schema.json",
"title": "PostAuthResumeWebhookExtension10Request",
"type": "object",
"required": ["method", "headers", "body"],
"additionalProperties": false,
"properties": {
"method": {
"type": "string",
"enum": ["POST"]
},
"headers": {
"type": "object",
"additionalProperties": false,
"required": ["Authorization", "Content-Type"],
"properties": {
"Authorization": { "type": "string" },
"Content-Type": { "type": "string", "enum": ["application/json"] }
}
},
"body": {
"type": "object",
"additionalProperties": false,
"required": ["event", "conversationId", "environment", "resumeRequest"],
"properties": {
"event": {
"type": "string",
"enum": ["post-auth-resume-event-1.0"]
},
"conversationId": {
"type": "string",
"description": "Random ID identifying this particular login session. Should be used as a session key to persist state between post-auth and post-auth-resume."
},
"environment": {
"type": "string",
"enum": ["test", "production"]
},
"resumeRequest": {
"type": "object",
"additionalProperties": false,
"required": ["url"],
"properties": {
"url": { "type": "string" }
}
}
}
}
}
}
Loading
Loading