# SSO and SCIM

Sign your organisation's people in through its own identity provider, by SAML or OpenID Connect, and keep the account's users in step with SCIM.

Your organisation's people can sign in to Sendora with their usual work account, through your identity provider. Sendora connects to it by SAML 2.0 or OpenID Connect. With SCIM, your identity provider also adds people to the account and deactivates them when they leave.

This page is for whoever sets up the identity provider, often IT. It gives the values to copy, what Sendora reads from your provider, each provider's steps and the SCIM that Sendora serves.

## Before you start

- An administrator of the Sendora account works under [Account › Sign-in](https://app.sendora.se/account/sso). Instead, they can mail you a link to IT from there. It opens the same pages in your browser for seven days, without an account of your own. It works only in the first browser that opens it, and an administrator can revoke it.
- You need administrator rights in your identity provider.
- You need to publish a DNS record at your organisation's email domain.

## How the setup goes

1. Prove that the account controls your email domain, with one DNS record.
2. Create a connection to your identity provider. Its page shows Sendora's values to copy into your provider first, and asks for your provider's details last.
3. Test the sign-in. The test signs nobody in to Sendora.
4. An administrator switches the connection on. The sign-in screen then shows a button under the address field for each address at your domain.
5. If you want, require SSO, and connect SCIM.

An account can hold several connections, for example one per provider, up to the count in [Limits](https://sendora.se/docs/limits#sso-and-scim). Each covers the domains you attach to it.

## Prove your domain

Add the domain under [Account › Sign-in](https://app.sendora.se/account/sso). Sendora shows one TXT record, such as:

```
_sendora-sso.example.com  TXT  "sendora-sso=8f14e45fceea167a5a36dedd4bea2543"
```

Publish it, then check the domain. Keep the record, since Sendora checks it again every day. If it goes missing, the administrators get a mail with the record. After seven days without it, the domain is no longer verified, and a connection left with no verified domain switches off.

- A domain covers its exact name only. `mail.example.com` is a domain of its own.
- Only one Sendora account can verify a domain.
- A verified sending domain does not count. Sending as a domain is not the same as signing its people in.

## Connect by SAML

Each connection has its own values for your provider. The connection's page shows each one to copy.

| Value                                | Is                                                              |
| ------------------------------------ | --------------------------------------------------------------- |
| Entity ID                            | `https://app.sendora.se/dashboard/sso/saml/<connection id>`     |
| ACS URL (Assertion Consumer Service) | `https://app.sendora.se/dashboard/sso/saml/<connection id>/acs` |
| Metadata URL                         | The entity ID, which also serves Sendora's metadata             |
| Sign-on URL                          | `https://app.sendora.se/login?sso_domain=<your domain>`         |

Sendora signs its requests, and its metadata carries its certificate. If your provider reads metadata from a URL, give it Sendora's metadata URL, so it follows a new certificate by itself.

Give Sendora your provider's metadata, as a URL or as a file.

- Sendora reads a URL again every day and takes new certificates as your provider publishes them. So publish a new certificate in the metadata a day before your provider signs with it. If the URL cannot be read for three days, the administrators get a mail.
- Sendora reads a file once. When your provider changes its certificate, give the connection the new metadata. On a connection that is on, it takes effect once a test with it passes.

Your provider must sign the response or the assertion, with RSA and SHA-256 or stronger. It must send the person's email address, and it should send their name.

- Sendora takes the address from the first of these attributes that the assertion carries: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`, `email`, `mail` or `urn:oid:0.9.2342.19200300.100.1.3`. Without any of them, it takes the NameID.
- It takes the name from `http://schemas.microsoft.com/identity/claims/displayname`, `displayName` or `urn:oid:2.16.840.1.113730.3.1.241`.
- Without a display name, it joins a given name and a surname: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname` and `.../surname`, `givenName` and `sn`, `firstName` and `lastName`, or `urn:oid:2.5.4.42` and `urn:oid:2.5.4.4`.
- Without any name, the sign-in still works, and the user has no name in Sendora.
- The NameID is who the person is to Sendora, so keep it the same for each person. If one changes, an administrator can unlink the person under [Users](https://app.sendora.se/users), and their next sign-in links them again.

## Connect by OpenID Connect

Create a web app with the authorization code flow in your provider, with these values.

| Value        | Is                                                                   |
| ------------ | -------------------------------------------------------------------- |
| Redirect URI | `https://app.sendora.se/dashboard/sso/oidc/<connection id>/callback` |
| Scopes       | `openid email profile`                                               |

Then give Sendora:

- the issuer's URL, whose `/.well-known/openid-configuration` Sendora must reach from the internet;
- the client ID and the client secret, sent as HTTP Basic (`client_secret_basic`) or in the form (`client_secret_post`);
- the date the secret expires, if it does. Sendora mails the administrators 30 and 7 days before.

Sendora uses PKCE and a nonce on every sign-in. It reads the person from the ID token, or from the userinfo endpoint when the ID token has no address.

- `sub` is who the person is to Sendora.
- `email` is the address. Sendora refuses one whose `email_verified` is false.
- The name is `name`, or else `given_name` and `family_name`. Without one, the sign-in still works.

## Each provider's steps

The connection's page shows these steps too, with its own values filled in. Entra ID, Google Workspace, Okta and AD FS connect by SAML, and Keycloak by OpenID Connect.

### Microsoft Entra ID

1. In the Microsoft Entra admin center, as at least a Cloud Application Administrator, go to **Entra ID**, **Enterprise apps** and **All applications**. Choose **New application**, then **Create your own application**.
2. Name the app Sendora. Choose **Integrate any other application you don't find in the gallery (Non-gallery)**, then **Create**.
3. In the app, choose **Single sign-on** under **Manage**, then **SAML**. Under **Basic SAML Configuration**, choose **Edit**. Fill in **Identifier (Entity ID)**, **Reply URL (Assertion Consumer Service URL)** and **Sign on URL** with Sendora's values, and choose **Save**.
4. Leave **Attributes & Claims** as they are. The app already sends the address, the given name and the surname. A person with no address in Entra gets their user principal name, which works when it is their address.
5. Under **Properties**, set **Assignment required?** to **Yes**. Then under **Users and groups**, choose **Add user/group** and assign the people or groups who sign in to Sendora. Assigning groups needs Entra ID P1 or P2.
6. Back under **Single sign-on**, copy **App Federation Metadata Url** from **SAML Certificates**, and give it to Sendora.

Then test the sign-in in Sendora. Entra's own **Test** button works only once the connection is on.

### Google Workspace

1. In the Google Admin console, as a super administrator, go to **Apps** and **Web and mobile apps**. Choose **Add app**, then **Add custom SAML app**. Name it Sendora and choose **Continue**.
2. On **Google Identity Provider details**, choose **Download Metadata** under **Option 1**, and keep the file for Sendora. Choose **Continue**.
3. On **Service provider details**, fill in **ACS URL** and **Entity ID** with Sendora's values, and leave **Start URL** empty. Choose **EMAIL** as the **Name ID format** and **Basic Information > Primary email** as the **Name ID**. Choose **Continue**.
4. Under **Attribute mapping**, map **Basic Information > First Name** to `firstName` and **Last Name** to `lastName`. Choose **Finish**.
5. A new app is off. Open it, choose **User access**, and turn it on for everyone or for the groups who sign in to Sendora. Choose **Save**.
6. Give Sendora the metadata file.

The change can take up to a day to apply, though usually less. Google's **Test SAML login** cannot complete Sendora's test, so test the sign-in in Sendora.

### Okta

1. In the Okta Admin Console, go to **Applications and Resources** and **Applications**. Choose **Create App Integration**.
2. Choose **SAML 2.0** as the **Sign-in method**, then **Next**. Name the app Sendora and choose **Next**.
3. Under **Configure SAML**, fill in **Single sign-on URL** with the ACS URL and **Audience URI (SP Entity ID)** with the entity ID. Leave **Use this for Recipient URL and Destination URL** ticked.
4. Choose **EmailAddress** as the **Name ID format** and **Email** as the **Application username**. Leave **Show Advanced Settings** as it is, and choose **Next**.
5. Under **Feedback**, tick **This is an internal app that we have created**, then choose **Finish**.
6. The app opens on its **Sign On** tab. Under **Attribute statements**, choose **Add expression**. Add `firstName` with the expression `user.profile.firstName`, and `lastName` with `user.profile.lastName`.
7. Under **Assignments**, assign the people or groups who sign in to Sendora.
8. Back on the **Sign On** tab, copy **Metadata URL** from **Metadata details**, and give it to Sendora.

### AD FS

1. In **AD FS Management**, choose **Relying Party Trusts**, then **Add Relying Party Trust** under **Actions**. Choose **Claims aware** and **Start**.
2. Choose **Import data about the relying party published online or on a local network**, and give it Sendora's metadata URL. AD FS then reads Sendora's other values itself. If AD FS cannot reach the internet, save the metadata from a browser and import it from the file instead.
3. Name the trust Sendora. Under **Choose Access Control Policy**, choose **Permit everyone** or **Permit specific group**. Go on to **Finish**, with **Configure claims issuance policy for this application** ticked.
4. Under **Issuance Transform Rules**, add a rule from the template **Send LDAP Attributes as Claims**, with **Active Directory** as the attribute store. Send **E-Mail-Addresses** as **E-Mail Address**, **Given-Name** as **Given Name** and **Surname** as **Surname**.
5. Add a rule from the template **Transform an Incoming Claim** that turns **E-Mail Address** into **Name ID**, with the format **Email**.
6. Open the trust's **Monitoring** tab, and keep **Monitor relying party** and **Automatically update relying party** ticked. AD FS then reads Sendora's metadata again when Sendora changes its certificate.
7. Give Sendora your federation service's metadata, at `https://<your federation service>/FederationMetadata/2007-06/FederationMetadata.xml`. If Sendora cannot reach it from the internet, save it from a browser and give Sendora the file.

### Keycloak

1. In the Keycloak admin console, choose your realm. Go to **Clients** and choose **Create client**.
2. Leave **Client type** as **OpenID Connect**, enter `sendora` as the **Client ID** and choose **Next**.
3. Turn on **Client authentication**, leave **Standard flow** ticked and choose **Next**.
4. Fill in **Valid redirect URIs** with Sendora's redirect URI and **Home URL** with the sign-on URL. Leave the other fields empty and choose **Save**.
5. Under the **Credentials** tab, copy **Client Secret**.
6. Give Sendora the realm's URL as the issuer, such as `https://keycloak.example.com/realms/example`, with the client ID and the secret. Sendora talks to that URL directly, so Keycloak must be reachable from the internet.

Sendora accepts only an address that Keycloak has verified. Turn on **Email verified** for each person, or **Trust Email** for people who come from LDAP or another identity provider.

## Test and switch on

Test the sign-in from the connection's page. The test opens your provider's sign-in and signs nobody in to Sendora. Its result shows what your provider sent: the id, the address and the name, and whether the address is at a verified domain. Only whoever started the test sees the result, for an hour.

| A failed test says      | Check                                                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `provider_error`        | Your provider stopped the sign-in. Check that the person may use the app.                                                             |
| `discovery_failed`      | Sendora could not read the issuer's configuration. Check the issuer's URL.                                                            |
| `token_exchange_failed` | Your provider refused the client ID or the secret.                                                                                    |
| `id_token_invalid`      | The ID token failed Sendora's checks. Check the issuer's URL and the client ID.                                                       |
| `userinfo_failed`       | Sendora could not read the userinfo endpoint. Try again shortly.                                                                      |
| `email_missing`         | Your provider sent no address. Let the app read the person's email.                                                                   |
| `email_unverified`      | Your provider says the address is not verified.                                                                                       |
| `response_invalid`      | Sendora could not accept the response. Check that your provider uses Sendora's metadata.                                              |
| `signature_invalid`     | The signature does not match the connection's certificate. Give the connection your provider's metadata again, and sign with SHA-256. |
| `response_expired`      | The response was not valid at this moment. Check your provider's clock.                                                               |
| `audience_mismatch`     | The response was meant for another service. Check Sendora's entity ID and ACS URL in your provider.                                   |
| `issuer_mismatch`       | The response came from another provider. Give the connection your provider's metadata again.                                          |

An administrator switches the connection on, once three things hold:

- the connection has a verified domain;
- a test has passed since its settings last changed;
- Sendora has approved the account.

Through a link to IT, you can set up, change and test a connection while it is off, and create SCIM tokens. You cannot switch a connection on, change one that is on, require SSO or revoke a token. Once a connection is on, a change of its provider settings waits for a test with the new settings to pass. So an edit cannot lock its people out. A certificate that your provider publishes in its metadata is taken as it is.

## Sign in

A person types their address on the sign-in screen. At a domain of a connection that is on, a button under the field starts the sign-in at your provider. Only an address at one of the connection's verified domains signs in through it.

A tile in Microsoft's My Apps or Okta's dashboard works too. Its response signs nobody in by itself, since Sendora starts every sign-in. Sendora sends the browser to the sign-in screen with the button, and one click there starts a sign-in that your provider's session completes.

The first time, Sendora finds the person's user:

- A user whom SCIM created, or whom this connection created, is linked at once.
- Any other user with the address, such as one an administrator invited, gets a mail first. Its link confirms who they are and lasts 15 minutes. If it runs out, signing in again mails a new one.
- A person with no user gets one as a member with no servers, if the connection creates users. Otherwise an administrator invites them first.

A sign-in through SSO lasts at most 12 hours. A person whose address is in another Sendora account cannot sign in here.

## Require SSO

An administrator can require SSO on a connection that is on. It covers the people at its domains and those linked to it.

- A password fails for everyone it covers.
- A member signs in only through your provider. A request for a mailed link gets the SSO button instead.
- An administrator keeps the mailed link, and that session lasts 12 hours. So while your provider is down, an administrator who can read their mail still signs in, and can switch the requirement off.
- Switching the requirement on ends these people's other sessions and their connected apps, such as an AI assistant.
- People at other domains, keys, SMTP and webhooks are unaffected.

If no administrator can sign in, [Sendora's support](https://sendora.se/contact) can lift the requirement.

## SCIM

With SCIM, your provider adds people to the account as members with no servers, keeps their names and addresses current, and deactivates them when they leave. A deactivated person cannot sign in in any way, and their sessions end at once. Roles and servers stay with the account's administrators.

### Connect your provider

An administrator, or you through a link to IT, creates a SCIM token under [Account › Sign-in](https://app.sendora.se/account/sso). Sendora shows it once. To replace a token, create the new one before you revoke the old, so provisioning never stops.

| Setting  | Value                                       |
| -------- | ------------------------------------------- |
| Base URL | `https://api.sendora.se/scim/v2`            |
| Token    | The SCIM token, `scim_…`, as a bearer token |

Every request carries `Authorization: Bearer scim_…`. Answers are `application/scim+json`, and errors use the error schema of RFC 7644.

In Microsoft Entra ID, provision from the enterprise app that signs your people in by SAML. An app that signs in by OpenID Connect cannot provision, so with one, create a separate enterprise app for provisioning alone, as in the first two of Entra's steps above.

1. In the enterprise app, open **Provisioning** and set the mode to **Automatic**.
2. Enter the base URL as **Tenant URL** and the token as **Secret Token**. Choose **Test Connection**, then **Save**.
3. Under **Mappings**, turn off **Provision Microsoft Entra ID Groups**, since Sendora keeps no groups. A group assigned to the app still provisions its members.
4. If your users' principal names are not their addresses, map **mail** to **userName** under the users' mapping. Sendora finds the people already in the account by their address, and creating one of them again fails with 409 `uniqueness`.
5. Turn provisioning on. It provisions the users assigned to the app, directly or through a group.

In Okta, use the SAML integration from above, since Okta offers SCIM only on SAML integrations.

1. Under the app's **General** tab, edit **App Settings**, choose **SCIM** as **Provisioning** and save.
2. Under the **Provisioning** tab, edit **Integration**. Enter the base URL as **SCIM connector base URL** and `userName` as **Unique identifier field for users**.
3. Under **Supported provisioning actions**, tick **Push New Users** and **Push Profile Updates**, and leave the rest unticked. Sendora keeps no groups.
4. Choose **HTTP Header** as **Authentication Mode**, and enter the token after **Bearer**. Choose **Test Connector Configuration**, then **Save**.
5. Under **To App**, choose **Edit**, enable **Create Users**, **Update User Attributes** and **Deactivate Users**, and save.
6. If people were assigned to the app before you turned provisioning on, choose **Provision User** under **Assignments**. Okta finds the people already in Sendora by their address.

AD FS and Keycloak have no SCIM client of their own. Google Workspace may not offer SCIM for a custom SAML app.

### Endpoints

| Endpoint                                                   | Does                                                 |
| ---------------------------------------------------------- | ---------------------------------------------------- |
| `GET /ServiceProviderConfig`, `/ResourceTypes`, `/Schemas` | Describe what Sendora serves                         |
| `GET /Users`                                               | Lists or filters, with `startIndex` and `count`      |
| `POST /Users`                                              | Creates a member with no servers                     |
| `GET /Users/:id`                                           | Reads one                                            |
| `PUT /Users/:id`                                           | Replaces the attributes it carries                   |
| `PATCH /Users/:id`                                         | Changes one                                          |
| `DELETE /Users/:id`                                        | Removes one                                          |
| `GET /Groups`                                              | Returns an empty list, since Sendora keeps no groups |

Any other `/Groups` request fails with 501. A filter takes `eq` on `userName`, `externalId`, `id` and `emails[type eq "work"].value`, joined by `and`. Anything else fails with 400 `invalidFilter`. A `userName` or an address matches in any case, and an `externalId` matches exactly.

SCIM sees only the account's users with an address at one of its verified domains. A consultant at another domain is invisible to it, so assign only people at your domains to the app. Creating anyone else fails with 400 `invalidValue`.

A user whom SCIM has not created or changed has their address as their `userName`. So your provider finds the people who were there first, when it looks them up by their address.

### The user

- `id` is the Sendora user's id.
- `userName` and `externalId` are kept as your provider sent them.
- The address is the primary or work email, or else the `userName`. It must be at a verified domain, or the request fails with 400 `invalidValue`.
- `name.formatted` sets the full name, or else the given and family names, or else `displayName`.
- `active` is false while the user is deactivated.
- `password`, `roles`, `groups` and `entitlements` are ignored.

### What each request does

- `POST` creates a member with no servers and mails nothing. An address that any Sendora account already holds fails with 409 `uniqueness`.
- `PUT` replaces each attribute it carries. One it leaves out keeps its value, `active` included.
- `PATCH` takes `add`, `replace` and `remove`, with or without a path. `active` may be a boolean or the string `"True"` or `"False"`. A path to an attribute that Sendora does not keep changes nothing. A request Sendora cannot read fails whole with 400.
- `active: false` deactivates the user and ends their sessions and connected apps. `active: true` lets them in again. Only SCIM deactivates a user, and only SCIM reactivates one.
- A new address moves a member's address, if it is free and at a verified domain. An administrator's address never changes through SCIM, and such a request fails with 400 `mutability`.
- `DELETE` removes the user. For the account's last administrator it fails with 409, and they stay deactivated instead.
- Deactivating the last active administrator still happens, since a leaver must lose access. Sendora's support then helps the account name a new one.

## When certificates and secrets expire

Sendora mails the account's administrators 30 and 7 days before a connection's last certificate expires, or its OIDC secret on the date you gave. Your provider's metadata may list more than one certificate, so the mail goes before the last of them expires. A connection that is off gets no mail.

With a metadata URL, a certificate that your provider publishes is taken without any step from you. With a file, give the connection the new metadata, as under [Connect by SAML](#connect-by-saml).

[Limits](https://sendora.se/docs/limits#sso-and-scim) lists the counts and rates for SSO and SCIM.
