Amazon Cognito

Managed user pools, custom OTP auth challenges, and the token lifecycle, notes from building passwordless auth for a real registration flow.

August 29, 20262 min read5 / 15

A managed user directory that sits outside your application. Cognito stores users, runs authentication flows, and issues tokens, so your app never has to own a users table, a password hash column, or a login endpoint.

Why it matters: the user base scales independently of the application itself, since Cognito is a separate managed service with its own scaling. It's billed by monthly active users, not by infrastructure you provision whether it's busy or not.

Custom auth challenges (passwordless OTP)

Cognito doesn't have a built-in "send an OTP by SMS" flow. It's built from three Lambda triggers that Cognito calls in sequence, called a custom authentication challenge.

  • Define Auth Challenge: the orchestrator. Decides what happens next: issue another challenge, or declare the user authenticated.
  • Create Auth Challenge: generates the actual challenge for this attempt (the OTP itself).
  • Verify Auth Challenge Response: checks whether what the user submitted matches.

One registration attempt walks this loop once: Define kicks it off, Create generates and sends the code, the user submits it, Verify confirms it. Once it passes, Cognito issues tokens and the user is authenticated.

Tokens

Cognito issues JWT tokens after a successful auth flow, small, signed pieces of text that prove identity without a database lookup on every request.

  • ID token: identifies the user.
  • Access token: sent along with API calls to prove that identity. Both typically expire in about an hour.
  • Refresh token: used behind the scenes (by a client library like Amplify) to get new ID/access tokens without the user having to log in again.

API Gateway integration

API Gateway routes can point straight at a Cognito user pool as their authorizer, no custom authorizer Lambda needed for a plain "is this token valid" check. A request with a missing, expired, or invalid token gets rejected with a 401 before it reaches any application code. See the token gets checked first for the full mechanics.

Where this showed up

Further Reading and Watching