> ## Documentation Index
> Fetch the complete documentation index at: https://docs.authsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OIDC providers - Redirect flow

> Authenticate users by redirecting them to an external provider such as Google, Apple, or any OAuth 2.0 / OIDC-compliant provider.

Authsignal SDKs can be used to implement OIDC provider authentication in the following scenarios.

1. [Sign in or sign up](#sign-in-or-sign-up). Authenticate users without a known user ID. Covers both new and returning users.
2. [Step-up authentication](#step-up-authentication). Re-verify a known user with a linked provider before a sensitive action.
3. [Enrol an OIDC provider](#enrol-an-oidc-provider). Link a provider to an existing user as an additional authentication factor.

<Note>
  OIDC providers are configured per-tenant with allowed redirect URLs and per-action restrictions.
</Note>

## How it works

To bind the client that starts a challenge to the client that completes it, the client SDK generates a `codeVerifier` and a hashed `codeChallenge`. The `codeChallenge` is sent to Authsignal when the client challenge starts and the `codeVerifier` is persisted locally. After the user is redirected back, the SDK returns the `codeVerifier` alongside the `authorizationToken` so the server can prove the challenge is being completed by the original client.

```mermaid theme={null}
sequenceDiagram
    participant C as Your client
    participant B as Your server
    participant A as Authsignal
    participant P as OIDC provider
    C->>B: Sign-in request
    B->>A: oidc.challenge()
    A->>B: authorizationToken
    B->>C: authorizationToken
    Note over C: SDK generates codeVerifier & codeChallenge<br/>and persists codeVerifier locally
    C->>A: oidc.challenge(authorizationToken)<br/>sends codeChallenge
    A->>C: authorizationUrl
    C->>P: Redirect to provider
    Note over P: User authenticates
    P->>A: Callback with authorization code
    Note over A: Token exchange & validation
    A->>C: Redirect to your app
    C->>C: oidc.handleCallback()
    Note over C: SDK returns authorizationToken<br/>and codeVerifier
    C->>B: authorizationToken, codeVerifier
    B->>A: verify(authorizationToken, codeVerifier)
    A->>B: Attributes, user & session
```

## SDK setup

### Server SDK

Initialize the Server SDK using your secret key from the [API keys page](https://portal.authsignal.com/organisations/tenants/api) and the [API URL](/sdks/server/overview#regions) for your region.

<CodeGroup>
  ```ts Node.js theme={null}
  import { Authsignal } from "@authsignal/node";

  const authsignal = new Authsignal({
    apiSecretKey: "YOUR_SECRET_KEY",
    apiUrl: "YOUR_API_URL",
  });
  ```

  ```csharp C# theme={null}
  using Authsignal;

  var authsignal = new AuthsignalClient(
      apiSecretKey: "YOUR_SECRET_KEY",
      apiUrl: "YOUR_API_URL"
  );
  ```

  ```java Java theme={null}
  import com.authsignal.AuthsignalClient;

  AuthsignalClient authsignal = new AuthsignalClient(
      "YOUR_SECRET_KEY",
      "YOUR_API_URL"
  );
  ```

  ```ruby Ruby theme={null}
  require 'authsignal'

  Authsignal.setup do |config|
      config.api_secret_key = ENV["YOUR_SECRET_KEY"]
      config.api_url = "YOUR_API_URL"
  end
  ```

  ```python Python theme={null}
  from authsignal.client import AuthsignalClient

  authsignal = AuthsignalClient(
      api_secret_key="YOUR_SECRET_KEY",
      api_url="YOUR_API_URL"
  )
  ```

  ```php PHP theme={null}
  Authsignal::setApiSecretKey("YOUR_SECRET_KEY");
  Authsignal::setApiUrl("YOUR_API_URL");
  ```

  ```go Go theme={null}
  import "github.com/authsignal/authsignalgo/v2/client"

  client := NewAuthsignalClient(
      "YOUR_SECRET_KEY",
      "YOUR_API_URL",
  )
  ```
</CodeGroup>

### Client SDK

Initialize the Client SDK with your tenant ID and the API URL for your region.

```ts theme={null}
import { Authsignal } from "@authsignal/browser";

const authsignal = new Authsignal({
  tenantId: "YOUR_TENANT_ID",
  baseUrl: "YOUR_API_URL",
});
```

## Sign in or sign up

<Note>**Scenario** - Authenticate a user when you don't have a user ID upfront, e.g. adding a "Sign in with Google" button to your app.</Note>

Your server initiates the challenge without a `userId`. After the user authenticates with the provider, your server calls `verify` to get the provider's identity attributes, then calls `identify` to associate the user with an ID in your system.

### 1. Initiate challenge

Call `oidc.challenge` on your server to start the OIDC provider flow. This returns an `authorizationToken` that your server passes back to the client.

<CodeGroup>
  ```ts Node.js theme={null}
  const { authorizationToken } = await authsignal.oidc.challenge({
    action: "signIn",
    provider: "google",
    redirectUrls: ["https://myapp.com/auth/callback"],
    clientId: "YOUR_APP_CLIENT_ID",
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Oidc.Challenge(new OidcChallengeRequest(
      Action: "signIn",
      Provider: "google",
      RedirectUrls: new[] { "https://myapp.com/auth/callback" },
      ClientId: "YOUR_APP_CLIENT_ID"
  ));
  ```

  ```java Java theme={null}
  OidcChallengeRequest request = new OidcChallengeRequest();
  request.action = "signIn";
  request.provider = "google";
  request.redirectUrls = new String[] { "https://myapp.com/auth/callback" };
  request.clientId = "YOUR_APP_CLIENT_ID";

  OidcChallengeResponse response = authsignal.oidc.challenge(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal::Oidc.challenge(
      action: "signIn",
      provider: "google",
      redirect_urls: ["https://myapp.com/auth/callback"],
      client_id: "YOUR_APP_CLIENT_ID",
  )
  ```

  ```python Python theme={null}
  response = authsignal.oidc.challenge(
      action="signIn",
      provider="google",
      redirect_urls=["https://myapp.com/auth/callback"],
      client_id="YOUR_APP_CLIENT_ID",
  )
  ```

  ```go Go theme={null}
  response, err := client.Oidc.Challenge(
      OidcChallengeRequest{
          Action: "signIn",
          Provider: "google",
          RedirectUrls: []string{"https://myapp.com/auth/callback"},
          ClientId: "YOUR_APP_CLIENT_ID",
      },
  )
  ```
</CodeGroup>

Return the `authorizationToken` from the response to your client.

### 2. Start the client challenge

On your client, call `oidc.challenge` with the `authorizationToken`. The SDK redirects the user to the provider's login page.

```ts theme={null}
await authsignal.oidc.challenge({ authorizationToken });
```

### 3. Handle callback

After the user authenticates with the provider, they are redirected back to one of your `redirectUrls`. Call `oidc.handleCallback` on your client to retrieve a short-lived `authorizationToken` representing the completed challenge along with the `codeVerifier`.

```ts theme={null}
const { authorizationToken, codeVerifier } = await authsignal.oidc.handleCallback();
```

Send the `authorizationToken` and `codeVerifier` to your server for verification.

### 4. Verify

On your server, call `verify` with the `authorizationToken` and `codeVerifier`.

<CodeGroup>
  ```ts Node.js theme={null}
  const { challengeId, user, attributes } = await authsignal.verify({
    authorizationToken,
    codeVerifier,
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Verify(new VerifyRequest(
      AuthorizationToken: authorizationToken,
      CodeVerifier: codeVerifier
  ));
  ```

  ```java Java theme={null}
  VerifyRequest request = new VerifyRequest();
  request.authorizationToken = authorizationToken;
  request.codeVerifier = codeVerifier;

  VerifyResponse response = authsignal.verify(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal.verify(
      authorization_token: authorization_token,
      code_verifier: code_verifier,
  )
  ```

  ```python Python theme={null}
  response = authsignal.verify(
      authorization_token=authorization_token,
      code_verifier=code_verifier,
  )
  ```

  ```go Go theme={null}
  response, err := client.Verify(
      VerifyRequest{
          AuthorizationToken: authorizationToken,
          CodeVerifier: codeVerifier,
      },
  )
  ```
</CodeGroup>

#### Returning user

If the user has previously signed in and their provider identity is already linked, the `verify` response will return `isIdentified: true` with session tokens. In this case, the user is already associated — skip the `identify` step and proceed with the session.

#### New user

If the user is new, the response includes `isIdentified: false` on the user, indicating no user ID has been associated yet. The `attributes` object contains the verified provider data such as `email`, `sub`, `emailVerified`, and `iss`. Call `identify` in the next step to complete the process.

### 5. Identify

Associate the user with a user ID. You can either use the Authsignal-generated user ID from the verify response, or provide your own.

**Option A: Use the Authsignal-generated user ID**

Use the `userId` returned in the verify response to confirm the Authsignal-generated ID.

<CodeGroup>
  ```ts Node.js theme={null}
  const { user: identifiedUser } = await authsignal.identify({
    challengeId,
    userId: user.userId,
  });
  ```

  ```csharp C# theme={null}
  var identifyResponse = await authsignal.Identify(new IdentifyRequest(
      ChallengeId: response.ChallengeId,
      UserId: response.User.UserId
  ));
  ```

  ```java Java theme={null}
  IdentifyRequest identifyRequest = new IdentifyRequest();
  identifyRequest.challengeId = response.challengeId;
  identifyRequest.userId = response.user.userId;

  IdentifyResponse identifyResponse = authsignal.identify(identifyRequest).get();
  ```

  ```ruby Ruby theme={null}
  identify_response = Authsignal.identify(
      challenge_id: response[:challenge_id],
      user_id: response[:user][:user_id],
  )
  ```

  ```python Python theme={null}
  identify_response = authsignal.identify(
      challenge_id=response.challenge_id,
      user_id=response.user.user_id,
  )
  ```

  ```go Go theme={null}
  identifyResponse, err := client.Identify(
      IdentifyRequest{
          ChallengeId: response.ChallengeId,
          UserId: response.User.UserId,
      },
  )
  ```
</CodeGroup>

**Option B: Bring your own user ID**

Look up or create the user in your system using the verified attributes, then pass your user ID to `identify`.

<CodeGroup>
  ```ts Node.js theme={null}
  const myUserId = await mySystem.findOrCreateUser(attributes.email);

  const { user: identifiedUser } = await authsignal.identify({
    challengeId,
    userId: myUserId,
  });
  ```

  ```csharp C# theme={null}
  var myUserId = await mySystem.FindOrCreateUser(response.Attributes.Email);

  var identifyResponse = await authsignal.Identify(new IdentifyRequest(
      ChallengeId: response.ChallengeId,
      UserId: myUserId
  ));
  ```

  ```java Java theme={null}
  String myUserId = mySystem.findOrCreateUser(response.attributes.email);

  IdentifyRequest identifyRequest = new IdentifyRequest();
  identifyRequest.challengeId = response.challengeId;
  identifyRequest.userId = myUserId;

  IdentifyResponse identifyResponse = authsignal.identify(identifyRequest).get();
  ```

  ```ruby Ruby theme={null}
  my_user_id = my_system.find_or_create_user(response[:attributes][:email])

  identify_response = Authsignal.identify(
      challenge_id: response[:challenge_id],
      user_id: my_user_id,
  )
  ```

  ```python Python theme={null}
  my_user_id = my_system.find_or_create_user(response.attributes.email)

  identify_response = authsignal.identify(
      challenge_id=response.challenge_id,
      user_id=my_user_id,
  )
  ```

  ```go Go theme={null}
  myUserId := mySystem.FindOrCreateUser(response.Attributes.Email)

  identifyResponse, err := client.Identify(
      IdentifyRequest{
          ChallengeId: response.ChallengeId,
          UserId: myUserId,
      },
  )
  ```
</CodeGroup>

## Step-up authentication

<Note>**Scenario** - Require a user to re-authenticate with a linked OIDC provider as an additional verification step. The user must already have the provider enrolled.</Note>

Use this flow when a user is already signed in and you want to verify their identity again before a sensitive action. The provider must already be enrolled for the user — if no provider is enrolled, the challenge will return an error. If the user authenticates with a different provider account than the one linked, the verification will fail.

### 1. Initiate challenge

<CodeGroup>
  ```ts Node.js theme={null}
  const { authorizationToken } = await authsignal.oidc.challenge({
    action: "transferFunds",
    userId: "my-user-id",
    provider: "google",
    redirectUrls: ["https://myapp.com/auth/callback"],
    clientId: "YOUR_APP_CLIENT_ID",
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Oidc.Challenge(new OidcChallengeRequest(
      Action: "transferFunds",
      UserId: "my-user-id",
      Provider: "google",
      RedirectUrls: new[] { "https://myapp.com/auth/callback" },
      ClientId: "YOUR_APP_CLIENT_ID"
  ));
  ```

  ```java Java theme={null}
  OidcChallengeRequest request = new OidcChallengeRequest();
  request.action = "transferFunds";
  request.userId = "my-user-id";
  request.provider = "google";
  request.redirectUrls = new String[] { "https://myapp.com/auth/callback" };
  request.clientId = "YOUR_APP_CLIENT_ID";

  OidcChallengeResponse response = authsignal.oidc.challenge(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal::Oidc.challenge(
      action: "transferFunds",
      user_id: "my-user-id",
      provider: "google",
      redirect_urls: ["https://myapp.com/auth/callback"],
      client_id: "YOUR_APP_CLIENT_ID",
  )
  ```

  ```python Python theme={null}
  response = authsignal.oidc.challenge(
      action="transferFunds",
      user_id="my-user-id",
      provider="google",
      redirect_urls=["https://myapp.com/auth/callback"],
      client_id="YOUR_APP_CLIENT_ID",
  )
  ```

  ```go Go theme={null}
  response, err := client.Oidc.Challenge(
      OidcChallengeRequest{
          Action: "transferFunds",
          UserId: "my-user-id",
          Provider: "google",
          RedirectUrls: []string{"https://myapp.com/auth/callback"},
          ClientId: "YOUR_APP_CLIENT_ID",
      },
  )
  ```
</CodeGroup>

Return the `authorizationToken` from the response to your client.

### 2. Start the client challenge

```ts theme={null}
await authsignal.oidc.challenge({ authorizationToken });
```

### 3. Handle callback

```ts theme={null}
const { authorizationToken, codeVerifier } = await authsignal.oidc.handleCallback();
```

### 4. Verify

<CodeGroup>
  ```ts Node.js theme={null}
  const { session, user, attributes } = await authsignal.verify({
    authorizationToken,
    codeVerifier,
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Verify(new VerifyRequest(
      AuthorizationToken: authorizationToken,
      CodeVerifier: codeVerifier
  ));
  ```

  ```java Java theme={null}
  VerifyRequest request = new VerifyRequest();
  request.authorizationToken = authorizationToken;
  request.codeVerifier = codeVerifier;

  VerifyResponse response = authsignal.verify(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal.verify(
      authorization_token: authorization_token,
      code_verifier: code_verifier,
  )
  ```

  ```python Python theme={null}
  response = authsignal.verify(
      authorization_token=authorization_token,
      code_verifier=code_verifier,
  )
  ```

  ```go Go theme={null}
  response, err := client.Verify(
      VerifyRequest{
          AuthorizationToken: authorizationToken,
          CodeVerifier: codeVerifier,
      },
  )
  ```
</CodeGroup>

The response includes `isIdentified: true` and a `session` object containing `accessToken` and `refreshToken`.

### Example response

```json theme={null}
{
  "verificationMethod": "OIDC_PROVIDER",
  "attributes": {
    "atHash": "abc123def456",
    "aud": "your-google-client-id.apps.googleusercontent.com",
    "sub": "109876543210987654321",
    "emailVerified": true,
    "azp": "your-google-client-id.apps.googleusercontent.com",
    "iss": "https://accounts.google.com",
    "hd": "example.com",
    "exp": 1776316088,
    "iat": 1776312488,
    "email": "user@example.com"
  },
  "action": {
    "state": "CHALLENGE_SUCCEEDED",
    "completedVerificationMethods": ["OIDC_PROVIDER"]
  },
  "user": {
    "userId": "my-user-id",
    "emailVerified": false,
    "phoneNumberVerified": false,
    "isIdentified": true
  },
  "session": {
    "accessToken": "eyJhbGciOiJSUzI1NiIs...",
    "refreshToken": "9e1e8769e887fb0a029d..."
  }
}
```

## Enrol an OIDC provider

<Note>**Scenario** - Link an OIDC provider to an existing user as an additional authentication factor. The user must be in an authenticated state.</Note>

Use this flow to let users add an OIDC provider to their account. This requires the `add:authenticators` scope to be provided in the challenge call.

### 1. Initiate challenge

The `add:authenticators` scope is required to enrol a new OIDC provider for an existing user. This scope should only be used **when the user is in an already authenticated state**. For more information on using scopes safely refer to our documentation on [authenticator binding](/advanced-usage/authenticator-binding).

<CodeGroup>
  ```ts Node.js theme={null}
  const { authorizationToken } = await authsignal.oidc.challenge({
    action: "enrolProvider",
    userId: "my-user-id",
    provider: "google",
    redirectUrls: ["https://myapp.com/auth/callback"],
    clientId: "YOUR_APP_CLIENT_ID",
    scope: "add:authenticators",
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Oidc.Challenge(new OidcChallengeRequest(
      Action: "enrolProvider",
      UserId: "my-user-id",
      Provider: "google",
      RedirectUrls: new[] { "https://myapp.com/auth/callback" },
      ClientId: "YOUR_APP_CLIENT_ID",
      Scope: "add:authenticators"
  ));
  ```

  ```java Java theme={null}
  OidcChallengeRequest request = new OidcChallengeRequest();
  request.action = "enrolProvider";
  request.userId = "my-user-id";
  request.provider = "google";
  request.redirectUrls = new String[] { "https://myapp.com/auth/callback" };
  request.clientId = "YOUR_APP_CLIENT_ID";
  request.scope = "add:authenticators";

  OidcChallengeResponse response = authsignal.oidc.challenge(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal::Oidc.challenge(
      action: "enrolProvider",
      user_id: "my-user-id",
      provider: "google",
      redirect_urls: ["https://myapp.com/auth/callback"],
      client_id: "YOUR_APP_CLIENT_ID",
      scope: "add:authenticators",
  )
  ```

  ```python Python theme={null}
  response = authsignal.oidc.challenge(
      action="enrolProvider",
      user_id="my-user-id",
      provider="google",
      redirect_urls=["https://myapp.com/auth/callback"],
      client_id="YOUR_APP_CLIENT_ID",
      scope="add:authenticators",
  )
  ```

  ```go Go theme={null}
  response, err := client.Oidc.Challenge(
      OidcChallengeRequest{
          Action: "enrolProvider",
          UserId: "my-user-id",
          Provider: "google",
          RedirectUrls: []string{"https://myapp.com/auth/callback"},
          ClientId: "YOUR_APP_CLIENT_ID",
          Scope: "add:authenticators",
      },
  )
  ```
</CodeGroup>

Return the `authorizationToken` from the response to your client.

### 2. Start the client challenge

```ts theme={null}
await authsignal.oidc.challenge({ authorizationToken });
```

### 3. Handle callback

```ts theme={null}
const { authorizationToken, codeVerifier } = await authsignal.oidc.handleCallback();
```

### 4. Verify

<CodeGroup>
  ```ts Node.js theme={null}
  const { user, attributes } = await authsignal.verify({
    authorizationToken,
    codeVerifier,
  });
  ```

  ```csharp C# theme={null}
  var response = await authsignal.Verify(new VerifyRequest(
      AuthorizationToken: authorizationToken,
      CodeVerifier: codeVerifier
  ));
  ```

  ```java Java theme={null}
  VerifyRequest request = new VerifyRequest();
  request.authorizationToken = authorizationToken;
  request.codeVerifier = codeVerifier;

  VerifyResponse response = authsignal.verify(request).get();
  ```

  ```ruby Ruby theme={null}
  response = Authsignal.verify(
      authorization_token: authorization_token,
      code_verifier: code_verifier,
  )
  ```

  ```python Python theme={null}
  response = authsignal.verify(
      authorization_token=authorization_token,
      code_verifier=code_verifier,
  )
  ```

  ```go Go theme={null}
  response, err := client.Verify(
      VerifyRequest{
          AuthorizationToken: authorizationToken,
          CodeVerifier: codeVerifier,
      },
  )
  ```
</CodeGroup>

The OIDC provider is now linked to the user.

<Warning>
  If the OIDC provider is already linked to a different user, the challenge will fail with an error.
  Each provider identity (identified by the provider's `sub` or equivalent identifier) can only be linked to one user at a time.
</Warning>
