# Sign customers in to your storefront

> Register, sign in and verify your customers, refresh their tokens, and add two-factor authentication and social sign-in.

Source: https://docs.coritan.com/organizations/storefront/customer-sign-in/

Your customers have accounts with your organization, separate from Coritan accounts. Your storefront creates them and signs them in through `/auth/...`, and gets back a customer token that every `/portal/...` call takes as `Authorization: Bearer`.

## Before you begin

- Choose how your organization's email leaves under **Outgoing email (SMTP)** in **Settings**. We send your customers welcome, verification and password reset emails. See [Change organization settings](/organizations/settings/).
- Set your **Custom domain** in the same place. Emails link to pages on it, which your storefront must serve: `/verify-email?id=...&token=...`, `/reset-password?token=...` and `/billing/invoices/{invoice_id}`. Social sign-in returns to `/auth/callback`.
- If `GET /storefront/branding` reports `turnstile.enabled`, show the Cloudflare Turnstile challenge on your sign-up form and send its answer with the request.

## Register a customer

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/auth/register" \
  -H "Content-Type: application/json" \
  -d '{"email": "alex@example.com", "password": "a long passphrase", "username": "alex_builds", "first_name": "Alex", "turnstile_token": "..."}'
```

`email` and `password` are required, and the password needs at least 8 characters. `username`, `first_name`, `last_name`, `company` and `phone` are optional. Without a `username` we generate one. `GET /auth/username-available?username=...` says whether a name is free, and why not in `reason`, while the customer types.

A username has 3–20 letters, digits and underscores. Names that pass for staff, and names our moderation filter refuses, are not available. A customer can change theirs once every 7 days.

The answer is `201` with the customer's tokens, as for signing in. We also send your welcome email and an email to confirm the address, and your [webhooks](/organizations/webhooks/) receive `customer.created`. Confirming the address is optional: nothing on the account waits for it.

## Sign a customer in

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"identifier": "alex@example.com", "password": "a long passphrase"}'
```

`identifier` takes an email address or a username. The answer is one of two shapes:

- A session: `access_token`, `refresh_token`, `token_type` and `expires_in`. The access token lasts 24 hours and the refresh token 30 days.
- A two-factor step: `mfa_token` with `mfa_required: true` when the customer has two-factor authentication on, or `mfa_setup_required: true` when your organization requires it and the customer has not set it up. Continue under [Two-factor authentication](#two-factor-authentication).

Only `active` customers can sign in. A customer who registered with a social account has no password until they set one.

## Keep the session going

- Before the access token expires, send `{"refresh_token": "..."}` to `POST /auth/refresh`. The answer is a new pair, and the old tokens stop working at once.
- `POST /auth/logout` signs out the session whose token it carries.
- `GET /auth/sessions` lists where the customer is signed in, with the address, country, browser and expiry of each. `current` marks the one asking, and `support_session` marks a session your staff opened to look at the account.
- `POST /auth/sessions/revoke-others` signs out every other session and answers with how many it ended.

`GET /auth/me` returns the customer's profile. `PATCH /auth/me` changes `first_name`, `last_name`, `company`, `phone` or `chat_handle` (the username). To change the password, send `password` with `current_password`.

## Two-factor authentication

Customers turn on two-factor authentication with an authenticator app. To require it of every customer, turn it on in your staff console's settings. See [Manage the staff team and console settings](/organizations/staff-console/team-and-settings/).

1. `POST /auth/mfa/setup` returns a `secret`, an `otpauth_uri` and a `qr_svg` to show.
2. The customer scans it and sends a code to `POST /auth/mfa/enable` as `{"code": "123456"}`. The answer holds 10 `recovery_codes`, shown once. When the request carried an `mfa_token`, it also holds the session in `tokens`.
3. From then on, signing in answers with an `mfa_token`. Send it as the bearer token to `POST /auth/mfa/verify` with a code from the app or a recovery code, and the answer is the session.

An `mfa_token` works only on the `/auth/mfa` routes. It lasts 5 minutes for a sign-in, and 30 minutes for setting up when your organization requires two-factor authentication.

`GET /auth/mfa` says whether it is on and how many recovery codes are left. `POST /auth/mfa/recovery-codes` replaces the codes, given a current code. `POST /auth/mfa/disable` turns it off, given a code and the password when the account has one. It is refused while your organization requires two-factor authentication; your staff can reset a customer's instead.

## Social sign-in

Coritan configures the providers. `GET /auth/oauth/providers` lists the ones your storefront can offer, from `discord`, `github`, `google` and `microsoft`.

1. Call `GET /auth/oauth/{provider}/authorize` and send the customer to the `authorization_url` it returns.
2. After the provider, we send the customer back to `/auth/callback` on your storefront with the result in the URL fragment: `access_token`, `refresh_token` and `expires_in`, or `mfa_token` and its flags, or `error`. `new_account=true` marks a customer we just registered.

We return the customer only to a storefront address that Coritan has registered for your organization. Contact [support](https://www.coritan.com/dashboard/support) to register yours before you offer social sign-in.

A signed-in customer can link another provider by calling `authorize` with their token. When a provider's email matches an existing customer, we refuse with `account_exists_login_required`: the customer signs in with their password, then links the provider. `GET /auth/oauth/connections` lists the linked providers, and `DELETE /auth/oauth/connections/{provider}` removes one. `POST /auth/oauth/set-password` gives a social-only account a password: 8–128 characters with a lowercase letter, an uppercase letter, a digit and a symbol.

## Verify an address and reset a password

- The confirmation email links to `/verify-email?id=...&token=...`. Post both values to `POST /auth/verify-email` as `customer_id` and `token`. A link works for 24 hours, and only the newest one works. `POST /auth/resend-verification` sends another; `sent: false` with `email_unavailable` means your organization cannot send email.
- `POST /auth/forgot-password` with `email` sends a reset link to `/reset-password?token=...`. The answer is the same whether or not the account exists. Post the token and the new `password` to `POST /auth/reset-password` within 2 hours. The reset signs the customer out everywhere.

## Result

The customer has a session your storefront keeps in the browser and sends to every `/portal/...` call. The customer appears on the [Customers](/organizations/customers/) tab.

## Troubleshooting

`401 Invalid credentials`
: The identifier or the password is wrong, or the customer is not `active`.

`401 This account uses social login ...`
: The customer registered with a social account. They sign in with it, or set a password from their account.

`409 Email already registered` or `409 That username is already taken.`
: Sign the customer in instead, or choose another username.

`403 This email address cannot be used to register.`
: Your staff banned an account with that address.

`403 Customer limit reached (100/100)`
: Your organization holds as many customers as it may. Ask [support](https://www.coritan.com/dashboard/support) to raise the limit.

`401 Invalid refresh token` or `401 Refresh token expired`
: The refresh token was already used, revoked or is older than 30 days. Sign the customer in again.

`401` with `mfa_required`
: The token is still waiting for the second factor. Finish with `POST /auth/mfa/verify`.

`403 Account is suspended or closed` or `403 We have closed this account.`
: Your staff suspended, closed or banned the customer.

`400 Invalid reset token` or `400 Reset token expired`
: The link is wrong, was replaced by a newer one, or is more than 2 hours old. Ask for a new one.

## Related

- [Build the customer account area](/organizations/storefront/customer-portal/)
- [Manage customers](/organizations/customers/)
- [Build a storefront on the Organization API](/organizations/storefront/)

## API

- `POST /api/v1/orgs/{org_slug}/auth/forgot-password`: Issue a password-reset token stored on the customer metadata (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-forgot-password)
- `POST /api/v1/orgs/{org_slug}/auth/login`: Password sign-in with an email address or a username (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-login)
- `POST /api/v1/orgs/{org_slug}/auth/logout`: Revoke the current access token (and its refresh twin if present) (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-logout)
- `GET /api/v1/orgs/{org_slug}/auth/me`: Customer profile (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-me)
- `PATCH /api/v1/orgs/{org_slug}/auth/me`: Update customer profile (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-patch-api-v1-orgs-org-slug-auth-me)
- `GET /api/v1/orgs/{org_slug}/auth/mfa`: Customer MFA status (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-mfa)
- `POST /api/v1/orgs/{org_slug}/auth/mfa/disable`: Turn the factor off with a current code, and with the password when the account has one (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-mfa-disable)
- `POST /api/v1/orgs/{org_slug}/auth/mfa/enable`: Customer MFA enable (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-mfa-enable)
- `POST /api/v1/orgs/{org_slug}/auth/mfa/recovery-codes`: Customer MFA regenerate recovery codes (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-mfa-recovery-codes)
- `POST /api/v1/orgs/{org_slug}/auth/mfa/setup`: Customer MFA setup (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-mfa-setup)
- `POST /api/v1/orgs/{org_slug}/auth/mfa/verify`: Customer MFA verify (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-mfa-verify)
- `GET /api/v1/orgs/{org_slug}/auth/oauth/connections`: List OAuth connections (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-oauth-connections)
- `DELETE /api/v1/orgs/{org_slug}/auth/oauth/connections/{provider}`: Disconnect OAuth provider (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-delete-api-v1-orgs-org-slug-auth-oauth-connections-provider)
- `GET /api/v1/orgs/{org_slug}/auth/oauth/providers`: Public: which social providers are configured for storefront login (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-oauth-providers)
- `POST /api/v1/orgs/{org_slug}/auth/oauth/set-password`: OAuth set password (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-oauth-set-password)
- `GET /api/v1/orgs/{org_slug}/auth/oauth/{provider}/authorize`: OAuth authorize (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-oauth-provider-authorize)
- `GET /api/v1/orgs/{org_slug}/auth/oauth/{provider}/callback`: OAuth callback (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-oauth-provider-callback)
- `POST /api/v1/orgs/{org_slug}/auth/refresh`: Customer refresh token (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-refresh)
- `POST /api/v1/orgs/{org_slug}/auth/register`: Self-service customer signup for the org storefront (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-register)
- `POST /api/v1/orgs/{org_slug}/auth/resend-verification`: Send another confirmation link to the signed-in customer's own address (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-resend-verification)
- `POST /api/v1/orgs/{org_slug}/auth/reset-password`: Customer reset password (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-reset-password)
- `GET /api/v1/orgs/{org_slug}/auth/sessions`: Where this account is currently signed in (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-sessions)
- `POST /api/v1/orgs/{org_slug}/auth/sessions/revoke-others`: Sign out everywhere except here (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-sessions-revoke-others)
- `GET /api/v1/orgs/{org_slug}/auth/username-available`: Whether a username can be taken at signup, and why not when it cannot (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-get-api-v1-orgs-org-slug-auth-username-available)
- `POST /api/v1/orgs/{org_slug}/auth/verify-email`: Confirm an address from an emailed link (https://docs.coritan.com/api/reference/organizations/customer-authentication/auth/#op-post-api-v1-orgs-org-slug-auth-verify-email)
