Skip to content
Draft
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [5.8.0]

- `/appid-<appId>/<tenantId>/recipe/saml/clients` PUT:
- Adds an optional `attributeMapping` (`userId`, `email`, `firstName`, `lastName`, each an ordered list of 1-10 assertion claim names; `NameID` is the Subject NameID). Invalid mappings return a 400 naming the key.
- `acsURLs` (documented since 5.4.1) can now be cleared with `null`; `[]` also clears it.
- Both fields are required once set: an update of a client that has either field must send it (its current value keeps it, `null` / `[]` / `{}` clears it); leaving the key out returns a 400 naming the field. Requests on CDI < 5.8 keep the stored value instead.
- The client object (PUT response and `/recipe/saml/clients/list`) includes `acsURLs` and `attributeMapping` when set.
- `/appid-<appId>/<tenantId>/recipe/saml/user` POST: for clients with an `attributeMapping`, `sub` and `email` are resolved through it (first listed claim whose first value is non-blank; blank values fall through, unlike the built-in rules), and `firstName` / `lastName` are returned when mapped and found.

## [5.7.0]

- `/appid-<appId>/<tenantId>/users/count` GET: a single-tenant count without `includeRecipeIds` is now served from the user lifecycle ledger (anchored snapshot plus the changes since) by default, and the response always carries `approximate` and `asOf`. The `allowApproximate` query param is deprecated and ignored. Counts across all tenants or filtered by recipe are still computed exactly (`approximate: false`).
Expand Down
94 changes: 88 additions & 6 deletions api_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ info:

Those that do not have `<tenantId>` in the path will enforce that the API is called from `public` tenant only.

version: 5.7.0
version: 5.8.0
title: Core Driver Interface
contact:
email: team@supertokens.io
Expand Down Expand Up @@ -8196,6 +8196,8 @@ paths:

If `clientId` is omitted, a new client is created with a generated id (`st_saml_<uuid>`). If it matches an existing client, that client is replaced with the given values.
Requires the SAML feature to be enabled for the app (402 otherwise).

`acsURLs` and `attributeMapping` are required once set: when updating a client that has a value for either field, the request must include that key - its current value keeps it, `null` (or an empty array / object) clears it. Leaving the key out returns a 400 naming the field. On a client that never had the field set, leaving it out is fine.
operationId: createOrUpdateSamlClient
parameters:
- $ref: '#/components/parameters/samlRid'
Expand Down Expand Up @@ -8235,6 +8237,18 @@ paths:
type: boolean
description: Whether AuthnRequests are signed with the core's SP key. Defaults to true.
default: true
acsURLs:
type: array
nullable: true
description: |
Optional allow-list of Assertion Consumer Service URLs. When set, the `acsURL` given to `/recipe/saml/login` must be a member, and the `Destination` / `SubjectConfirmationData.Recipient` of an IdP-initiated response must be a member when present.
Required (at least one URL) when `allowIDPInitiatedLogin` is true; IdP-initiated logins for a client without one are refused.
Required once set (see above). `null` and `[]` clear it. Entries must be strings that are not empty or whitespace-only.
items:
type: string
example: ["https://api.example.com/auth/saml/callback"]
attributeMapping:
$ref: '#/components/schemas/samlAttributeMapping'
required:
- redirectURIs
- defaultRedirectURI
Expand Down Expand Up @@ -8507,6 +8521,8 @@ paths:
description: |
Exchange the single-use `code` returned by `/recipe/saml/callback` (passed as `accessToken`) for the authenticated user's SAML claims.
The code is removed on first use; a second call returns `INVALID_TOKEN_ERROR`.

For a client with an `attributeMapping`, `sub`, `email`, `firstName` and `lastName` are resolved through it at the time of this call (using the client's current mapping, with the default lists for fields it leaves out, skipping blank values). A client without a mapping uses the built-in rules described on each field, which take the first present claim as is.
operationId: getSamlUserInfo
parameters:
- $ref: '#/components/parameters/samlRid'
Expand Down Expand Up @@ -8541,15 +8557,23 @@ paths:
type: string
nullable: true
description: |
The user's id at the IdP. Taken from the first of: the Subject `NameID`,
The user's id at the IdP. For a client with an `attributeMapping`, resolved through `attributeMapping.userId` (default list: the Subject `NameID`,
`http://schemas.microsoft.com/identity/claims/objectidentifier`,
`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name`. `null` if none is present.
`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name`), skipping blank values.
For a client without one, the built-in rule: the first of those claims that is present, taken as is (a blank `NameID` gives an empty string). `null` if none is present.
email:
type: string
nullable: true
description: |
Taken from the first of: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`,
`mail`, `email`, or the Subject `NameID` when it contains an `@`. `null` if none is present.
For a client with an `attributeMapping`, resolved through `attributeMapping.email` (default list: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`,
`mail`, `email`, or the Subject `NameID` when it contains an `@`), skipping blank values.
For a client without one, the built-in rule: the first of those claims that is present, taken as is. `null` if none is present.
firstName:
type: string
description: Only present when the client's `attributeMapping.firstName` is set and one of its claims is found
lastName:
type: string
description: Only present when the client's `attributeMapping.lastName` is set and one of its claims is found
aud:
type: string
description: The SAML client id the code was issued for
Expand Down Expand Up @@ -9839,7 +9863,7 @@ components:
name: cdi-version
in: header
description: X.Y of the X.Y.Z CDI version.
example: '5.7'
example: '5.8'
schema:
type: string

Expand Down Expand Up @@ -10328,6 +10352,15 @@ components:
type: boolean
enableRequestSigning:
type: boolean
acsURLs:
type: array
description: Only present if set
items:
type: string
attributeMapping:
description: Only present if set
allOf:
- $ref: '#/components/schemas/samlAttributeMapping'
required:
- clientId
- defaultRedirectURI
Expand All @@ -10336,6 +10369,55 @@ components:
- allowIDPInitiatedLogin
- enableRequestSigning

samlAttributeMapping:
type: object
nullable: true
description: |
Which assertion claims supply the user's id, email, first name and last name, as ordered fallback lists of claim names (attribute `Name`s; `NameID` is the Subject NameID). For each field the first listed claim whose first value is non-blank wins (only a claim's first value is considered). For `email`, `NameID` only counts if it contains an `@`.

Fields left out use the defaults:
- `userId`: `NameID`, `http://schemas.microsoft.com/identity/claims/objectidentifier`, `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name`
- `email`: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`, `mail`, `email`, `NameID`
- `firstName`, `lastName`: none

Each list has 1 to 10 names; each name is a non-empty string of at most 512 characters (UTF-16 code units) with no leading or trailing whitespace (names match claims exactly and case-sensitively). Unknown keys are rejected. Invalid mappings return a 400 naming the key.
`null` and `{}` mean "no mapping": the client keeps the built-in rules, which take the first *present* claim of the same default chains as is - so a blank `NameID` yields an empty `sub` without a mapping, but falls through to the next claim with one. Note that changing `userId` on a client that already has users changes those users' ids.
properties:
userId:
type: array
minItems: 1
maxItems: 10
items:
type: string
maxLength: 512
email:
type: array
minItems: 1
maxItems: 10
items:
type: string
maxLength: 512
firstName:
type: array
minItems: 1
maxItems: 10
items:
type: string
maxLength: 512
lastName:
type: array
minItems: 1
maxItems: 10
items:
type: string
maxLength: 512
additionalProperties: false
example:
userId: ["id", "NameID"]
email: ["email", "NameID"]
firstName: ["firstName"]
lastName: ["lastName"]

userMetadata:
type: object
description: should be a JSON object (not a JSON literal nor an array)
Expand Down
Loading