# Switch from Google to signo: any OpenID Connect library

signo works with any library that supports OpenID Connect discovery and the authorization code flow. signo's own browser tests sign in to a demo site built with openid-client 6.8.8, a certified OpenID Connect library, and the code below comes from that demo.

The examples use `login.example.com` for signo and `example.com` for your site. On the public signo, the issuer is `https://usesigno.co.uk`.

## 1. Register your site with signo

On the public signo, sign in at https://usesigno.co.uk/account/sites and add your site with its callback URL. On your own signo, run `signo clients add` on its server, as in [Self-hosting](https://usesigno.co.uk/docs/self-hosting.md). Either way you get an issuer, a client ID and a client secret.

## 2. What your library needs

| Setting | Value |
|---|---|
| Discovery | `https://login.example.com/.well-known/openid-configuration` |
| Flow | authorization code (`response_type=code`) |
| PKCE | required, with `S256` |
| Scopes | `openid email profile` |
| Client authentication | HTTP Basic with the client secret (`client_secret_basic`) |
| Callback URLs | https, except on `localhost` |

The ID token carries `email`, `email_verified` and `name`, as Google's does, so code that reads them from Google's ID token keeps working. The userinfo endpoint at `/me` returns the same claims.

## 3. Example with openid-client

```ts
import * as oidc from 'openid-client';

const config = await oidc.discovery(
  new URL('https://login.example.com'),
  process.env.SIGNO_CLIENT_ID!,
  undefined,
  oidc.ClientSecretBasic(process.env.SIGNO_CLIENT_SECRET!),
);

// Sign-in: send the person to signo.
const verifier = oidc.randomPKCECodeVerifier();
const state = oidc.randomState();
const nonce = oidc.randomNonce();
const url = oidc.buildAuthorizationUrl(config, {
  redirect_uri: 'https://example.com/callback',
  scope: 'openid email profile',
  code_challenge: await oidc.calculatePKCECodeChallenge(verifier),
  code_challenge_method: 'S256',
  state,
  nonce,
});

// Callback: exchange the code. Keep verifier, state and nonce in the person's session in between;
// currentUrl is the full URL signo sent the person back to.
const tokens = await oidc.authorizationCodeGrant(config, currentUrl, {
  pkceCodeVerifier: verifier,
  expectedState: state,
  expectedNonce: nonce,
  idTokenExpected: true,
});
const { sub, email, name } = tokens.claims();
```

## 4. Signing out

To sign the person out of signo as well as your site, send them to the end-session endpoint with their ID token. The return URL must be one of the site's sign-out return URLs.

```ts
const logout = oidc.buildEndSessionUrl(config, {
  id_token_hint: idToken,
  post_logout_redirect_uri: 'https://example.com/',
});
```

## 5. Keep the people who signed up with Google

The person's signo `sub` differs from their Google one. Look existing users up by email the first time they sign in with signo. That is safe, because signo's emails are always verified.
