Skip to main content
NoResponse assesses each new account for abuse. It returns the current risk score of the account, from 0 to 100, where a higher score means more risk. It also returns a decision: allow, step_up, review or block. Clerk already knows facts that make the score more accurate. Examples are whether the user verified the email and when the user last passed a second factor. auth_context is the part of the assessment request that carries these facts. fromClerk is the Clerk adapter in each NoResponse SDK. It builds auth_context from the session claims that Clerk verified, and you add the facts from the Clerk user and session records. Your backend makes five calls:
  1. verifyToken: Clerk verifies the session token.
  2. users.getUser: gets the account age and the verified email and phone.
  3. sessions.getSession: gets the session age.
  4. fromClerk: turns the claims and the records into auth_context.
  5. assessments.create: NoResponse returns the assessment.

What Clerk can supply

What Clerk cannot supply

  • Which second factor the user passed. fva holds only ages, so mfa.methods stays empty. Only the age changes the score.
  • The time of the last login. The Clerk session token has no auth_time, so auth_time stays empty. session_created_at carries the age of the session instead.
  • The issue time of the token. A Clerk session token lasts 60 seconds, and Clerk issues a new one before it expires. So its iat is a refresh clock and not the age of the session. The adapter does not send it.
  • An assurance level. Clerk has no equivalent of assurance_level.

Install

TypeScript
The samples use @clerk/backend 3, clerk-backend-api 7 and clerk-sdk-go 2. Set two variables in the environment of your backend:
  • CLERK_SECRET_KEY, from the API keys page of the Clerk Dashboard.
  • NORESPONSE_API_KEY, a NoResponse key with the assess scope.
The TypeScript and Python NoResponse clients read NORESPONSE_API_KEY themselves. The Go client takes it as an argument. The quickstart tells you how to get a key.

Assess the new account

Assess each new account once. Do it on the first authenticated request for a Clerk user that your database does not know yet. Do it before you create your own records for the account or give it access. The API answers within 3 seconds. The session token comes from one of two places:
  • On a same-origin request, the __session cookie.
  • On a cross-origin request, the Authorization: Bearer token that your frontend gets from the Clerk getToken().
Replace https://app.example.com with the origins of your app, so that the code refuses a token made for another site. Clerk middleware can verify the session token first. Then pass its claims to the adapter and skip step 1. In @clerk/express the claims are getAuth(req).sessionClaims, and in Next.js they are (await auth()).sessionClaims.
TypeScript
ip is the public address of the client. Behind a load balancer, read it from the header that the load balancer sets, such as the last address that it appended to X-Forwarded-For. Never read it from a value that the client controls. The Clerk user id is the account_id. It is stable, and the Clerk API takes it when you act on the result later. The flow is account creation, so NoResponse waits up to 3000 ms for its full set of sources. To wait only for the sources that answer within 500 ms, send depth: "quick". The other sources still finish later, and their results reach you through the webhook.
Verified claims, never the tokenThe adapter takes the claims that the Clerk SDK returned after it verified the token. It never takes the token string. It sets verified: true and transport: "backend" to say that your backend did the verification. The API refuses an auth_context that holds a token.

Act on the answer

The answer starts with assessment.account, the one current risk of the account. Your code acts on assessment.account.risk.decision, the decision for the flow that you sent:
  • allow: let the account in.
  • step_up: ask for more proof first, such as a second factor.
  • review: hold the account until your team looks at it.
  • block: refuse the account. NoResponse answers block after you set a block threshold and the score reaches it.
  • Any other value: treat it as review.
assessment.account.risk.score and assessment.account.risk.reasons tell you why. assessment.detail holds the facts of this assessment, such as its status, the sources that did not answer and the summary facts.
  • assessment.account is absent while the first assessment of the account is still pending. Apply your fallback. Then take the final assessment from the webhook when it arrives.
  • If the call fails or times out, apply the same fallback.
Turn an assessment into a decision gives a full rule set to copy.

What the adapter sends

This is the auth_context for a new user who created an account under a minute ago. The user has a verified email and passed a second factor during account creation.
auth_context for a new user
That context fires four reasons. account.risk.reasons lists each one with its weight:
  • auth_new_session and auth_new_account raise the score for any new account.
  • auth_email_verified and auth_mfa_recent lower it. They make this account different from one with nothing verified.

Next steps

Turn an assessment into a decision

Rules for allow, step_up, review and block.

Pending, retries and idempotency

What a pending answer carries and how retries work.

Pass your auth session

Every auth_context field and the six auth reason codes.