Sign in with Clico
Add Clico sign-in to your application with OAuth 2.1 and OpenID Connect.
Use Clico as an identity provider so people can sign in to your application with their Clico account. This integration uses the OAuth 2.1 authorization-code flow and OpenID Connect (OIDC).
This guide is for application developers integrating Clico sign-in. OAuth client registration is currently managed by Clico operators; dynamic client registration is not available.
Availability: Ask your Clico integration contact to confirm that the OAuth Provider is enabled and to register your application before testing. Do not send a client secret through source control, chat, or a browser bundle.
What this integration provides
After a person signs in and approves the request, your app receives an authorization code and exchanges it for OIDC tokens. Use the ID token to establish the signed-in identity and the UserInfo endpoint to retrieve the claims your client is allowed to request.
OAuth access tokens issued for this sign-in flow do not grant access to Clico's existing API
endpoints. Do not send them to api.clico.co REST routes as bearer credentials.
Request client registration
Give your Clico contact:
- Your application name and a technical contact.
- Whether the app is a web app or a native app.
- Every exact callback URI. Production web callbacks must use HTTPS. Native apps can use HTTPS app links or a private-use URI scheme; loopback HTTP is allowed for local development.
- The OIDC scopes you need:
openid,profile,email, and optionallyoffline_access. - Whether the app can keep a client secret securely.
Clico will provide a client_id. A confidential web app can also receive a client_secret; keep it
on your server. Public clients, including browser-only and native apps, do not receive a secret and
must use PKCE.
The secret is shown only when the client is created or its secret is rotated. If it is lost, rotate it in the Clico dashboard and immediately replace the value in the client app's server environment; the previous secret stops working immediately.
Discover the provider
Fetch the metadata at runtime instead of assuming endpoint URLs:
| Metadata | URL |
|---|---|
| OAuth authorization-server metadata (RFC 8414) | https://auth.clico.co/.well-known/oauth-authorization-server/api/auth |
| OpenID Connect discovery | https://auth.clico.co/api/auth/.well-known/openid-configuration |
The expected issuer is https://auth.clico.co/api/auth. The metadata contains the authorization,
token, UserInfo, revocation, and JWKS endpoints. Validate the issuer against this exact value.
NextAuth.js v4
Clico currently advertises EdDSA as its ID-token signing algorithm. NextAuth.js v4's openid-client
defaults to RS256, so set the expected algorithm explicitly in the provider's client metadata.
Here is a complete confidential-web-provider example:
import type { OAuthConfig } from 'next-auth/providers/oauth'
const clicoIssuer = 'https://auth.clico.co/api/auth'
type ClicoProfile = {
sub: string
email: string
email_verified: boolean
name?: string | null
picture?: string | null
}
const clicoProvider: OAuthConfig<ClicoProfile> = {
id: 'clico',
name: 'Clico',
type: 'oauth',
wellKnown: `${clicoIssuer}/.well-known/openid-configuration`,
issuer: clicoIssuer,
clientId: process.env.CLICO_CLIENT_ID!,
clientSecret: process.env.CLICO_CLIENT_SECRET!,
client: {
token_endpoint_auth_method: 'client_secret_basic',
id_token_signed_response_alg: 'EdDSA',
},
authorization: { params: { scope: 'openid profile email' } },
checks: ['pkce', 'state', 'nonce'],
idToken: true,
userinfo: {
request: ({ client, tokens }) => {
if (!tokens.access_token) throw new Error('Clico did not return an access token')
return client.userinfo(tokens.access_token)
},
},
profile(profile) {
if (!profile.sub || !profile.email || profile.email_verified !== true) {
throw new Error('Clico must return a verified email address')
}
return {
id: profile.sub,
name: profile.name ?? null,
email: profile.email,
image: profile.picture ?? null,
}
},
}Set NEXTAUTH_URL to your public app origin. NextAuth derives the callback from the provider ID; for
the example above, register this exact URI with Clico:
https://www.example.com/api/auth/callback/clicoThe id_token_signed_response_alg setting belongs in the NextAuth provider configuration, not in
the Clico dashboard. Keep OIDC discovery, issuer validation, and the pkce, state, and nonce
checks enabled. Never put CLICO_CLIENT_SECRET in browser code or source control.
An OAuthCallback error containing unexpected JWT alg received, expected RS256, got: EdDSA means
the relying party is rejecting Clico's ID token before creating its local session. Match the
discovered id_token_signing_alg_values_supported; do not disable ID-token signature validation.
An existing account in the relying-party app is not required by OAuth. If the app uses a NextAuth adapter, NextAuth can create the local user on the first successful provider sign-in, subject to the app's own sign-in and email-verification policies.
If the browser already has a Clico session for a different person, choose Sign out and use another Clico account on the consent screen. Clico signs out the current session and preserves the pending authorization request while returning to sign-in. When Google is used, Clico asks Google to show its account chooser instead of silently reusing the currently active Google account.
Authorization-code flow
- Generate a cryptographically random
state, an OIDCnonce, and a PKCEcode_verifier. Calculatecode_challenge = BASE64URL(SHA256(code_verifier))without padding. For a Node.js server, you can generate these values with the built-in crypto module:
import { createHash, randomBytes } from 'node:crypto';
const randomValue = () => randomBytes(32).toString('base64url');
const state = randomValue();
const nonce = randomValue();
const codeVerifier = randomValue();
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');Store state, nonce, and codeVerifier in the user's short-lived server-side login session. For
browser-only and native apps, use a maintained OIDC client library and its system-browser flow.
2. Redirect the person's browser to the authorization_endpoint from the OIDC metadata with:
response_type=code
client_id=YOUR_CLIENT_ID
redirect_uri=YOUR_REGISTERED_CALLBACK_URI
scope=openid profile email
state=YOUR_RANDOM_STATE
nonce=YOUR_RANDOM_NONCE
code_challenge=YOUR_BASE64URL_SHA256_CHALLENGE
code_challenge_method=S256Include offline_access only if your app needs refresh tokens. Clico requires PKCE with S256, and
the redirect_uri must exactly match a URI registered for your client.
- Clico handles sign-in and consent on
auth.clico.co. After approval, Clico redirects to your registered callback withcode,state, andiss. Verifystatematches the value stored for this browser session, and verifyissishttps://auth.clico.co/api/auth. Handle anaccess_deniedresponse when the person declines. - Exchange the code from your server. Send the same
redirect_uriand the originalcode_verifier. Confidential clients authenticate with HTTP Basic; public clients send no client secret.
Example for a confidential web client:
curl --request POST "https://auth.clico.co/api/auth/oauth2/token" \
--user "$CLICO_CLIENT_ID:$CLICO_CLIENT_SECRET" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=$CODE" \
--data-urlencode "redirect_uri=$REDIRECT_URI" \
--data-urlencode "code_verifier=$CODE_VERIFIER"For a public client, omit --user and do not send a client_secret. The request must still include
the client_id, code, exact redirect_uri, and code_verifier.
curl --request POST "https://auth.clico.co/api/auth/oauth2/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "client_id=$CLICO_CLIENT_ID" \
--data-urlencode "code=$CODE" \
--data-urlencode "redirect_uri=$REDIRECT_URI" \
--data-urlencode "code_verifier=$CODE_VERIFIER"Validate tokens and read the profile
Use a maintained OIDC library to validate the ID token's signature using the provider's jwks_uri,
as well as its issuer, audience (client_id), expiration, and nonce. Do not treat decoding a JWT as
validation.
Use the access token with the userinfo_endpoint from discovery:
curl "https://auth.clico.co/api/auth/oauth2/userinfo" \
--header "Authorization: Bearer $ACCESS_TOKEN"Request only the scopes and claims your app needs. Store tokens server-side when possible. If
offline_access was approved, handle refresh-token rotation and securely replace the old refresh
token after each successful refresh. Revoke tokens through the revocation_endpoint from provider
metadata when disconnecting the Clico account.
Security checklist
- Use HTTPS in production and register exact redirect URIs.
- Use a fresh, unpredictable
statefor every authorization request and validate it on callback. - Use PKCE
S256for every client; never embed a confidential client secret in a browser or mobile app. - Validate ID tokens with a standard OIDC library, including
iss,aud,exp, andnonce. - Keep access and refresh tokens out of URLs, logs, analytics, and client-side persistent storage.
- Treat the OAuth access token as an OIDC/UserInfo credential, not as authorization for Clico REST APIs.