Skip to Content
ClicoDOCS

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 optionally offline_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:

MetadataURL
OAuth authorization-server metadata (RFC 8414)https://auth.clico.co/.well-known/oauth-authorization-server/api/auth
OpenID Connect discoveryhttps://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/clico

The 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

  1. Generate a cryptographically random state, an OIDC nonce, and a PKCE code_verifier. Calculate code_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=S256

Include 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.

  1. Clico handles sign-in and consent on auth.clico.co. After approval, Clico redirects to your registered callback with code, state, and iss. Verify state matches the value stored for this browser session, and verify iss is https://auth.clico.co/api/auth. Handle an access_denied response when the person declines.
  2. Exchange the code from your server. Send the same redirect_uri and the original code_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 state for every authorization request and validate it on callback.
  • Use PKCE S256 for 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, and nonce.
  • 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.