Skip to main content
Authsignal SDKs can be used to implement OAuth/OIDC provider authentication in the following scenarios.
  1. Sign in or sign up. Authenticate users without a known user ID. Covers both new and returning users.
  2. Step-up authentication. Re-verify a known user with a linked provider before a sensitive action.
  3. Enrol an OAuth/OIDC provider. Link a provider to an existing user as an additional authentication factor.
OAuth/OIDC providers are configured per-tenant with allowed redirect URLs and per-action restrictions.

How it works

SDK setup

Initialize the SDK using your secret key from the API keys page and the API URL for your region.
import { Authsignal } from "@authsignal/node";

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

Sign in or sign up

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.
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 oauth.challenge to start the OAuth/OIDC provider flow. This returns an authorizationUrl that your server passes back to the client.
const { authorizationUrl } = await authsignal.oauth.challenge({
  action: "signIn",
  provider: "google",
  redirectUrl: "https://myapp.com/auth/callback",
  clientId: "YOUR_APP_CLIENT_ID",
});
Return the authorizationUrl from the response to your client. Your client then redirects the user to this URL, which takes them to the provider’s login page.

2. Handle callback

After authenticating with the provider, the user is redirected back to your redirectUrl with challengeId and nonce query parameters. Your client should extract these values and send them to your server for verification.
const url = new URL(window.location.href);
const challengeId = url.searchParams.get("challengeId");
const nonce = url.searchParams.get("nonce");

3. Verify

On your server, call verify with the challengeId and nonce.
const { isVerified, user, attributes } = await authsignal.verify({
  challengeId,
  nonce,
});

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.

4. 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.
const { user: identifiedUser } = await authsignal.identify({
  challengeId,
  userId: user.userId,
});
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.
const myUserId = await mySystem.findOrCreateUser(attributes.email);

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

Step-up authentication

Scenario - Require a user to re-authenticate with a linked OAuth/OIDC provider as an additional verification step. The user must already have the provider enrolled.
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

const { authorizationUrl } = await authsignal.oauth.challenge({
  action: "transferFunds",
  userId: "my-user-id",
  provider: "google",
  redirectUrl: "https://myapp.com/auth/callback",
  clientId: "YOUR_APP_CLIENT_ID",
});
Return the authorizationUrl from the response to your client.

2. Handle callback

const url = new URL(window.location.href);
const challengeId = url.searchParams.get("challengeId");
const nonce = url.searchParams.get("nonce");

3. Verify

const { isVerified, session, user, attributes } = await authsignal.verify({
  challengeId,
  nonce,
});
The response includes isIdentified: true and a session object containing accessToken and refreshToken.

Example response

{
  "isVerified": true,
  "verificationMethod": "OAUTH_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": ["OAUTH_PROVIDER"]
  },
  "user": {
    "userId": "my-user-id",
    "emailVerified": false,
    "phoneNumberVerified": false,
    "isIdentified": true
  },
  "session": {
    "accessToken": "eyJhbGciOiJSUzI1NiIs...",
    "refreshToken": "9e1e8769e887fb0a029d..."
  }
}

Enrol an OAuth/OIDC provider

Scenario - Link an OAuth/OIDC provider to an existing user as an additional authentication factor. The user must be in an authenticated state.
Use this flow to let users add an OAuth/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 OAuth/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.
const { authorizationUrl } = await authsignal.oauth.challenge({
  action: "enrolProvider",
  userId: "my-user-id",
  provider: "google",
  redirectUrl: "https://myapp.com/auth/callback",
  clientId: "YOUR_APP_CLIENT_ID",
  scope: "add:authenticators",
});
Return the authorizationUrl from the response to your client.

2. Handle callback

const url = new URL(window.location.href);
const challengeId = url.searchParams.get("challengeId");
const nonce = url.searchParams.get("nonce");

3. Verify

const { isVerified, user, attributes } = await authsignal.verify({
  challengeId,
  nonce,
});
The OAuth/OIDC provider is now linked to the user.
If the OAuth/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.