Intermediate · Authentication and authorization · topic 34 of 54 · 30 min
Login with Google, OTP and passkeys
Explanation
Login with Google: the idea
OAuth 2.0 is a standard that lets a user give an app limited access to their account on another service, without giving away their password. OpenID Connect (OIDC) is a layer on top that also tells the app who the user is, through an ID token (a signed JWT).
The authorization code flow with PKCE
- The user taps "Continue with Google".
- Your backend makes a random state value and a random code_verifier, plus code_challenge = SHA-256 of the verifier. It saves them in the session.
- The browser is sent to Google with client_id, redirect_uri, scope=openid email profile, state and code_challenge.
- The user logs in at Google (not on your site) and agrees to share their email and profile.
- Google redirects back to your redirect_uri with ?code=...&state=....
- Your backend checks state, then sends the code, the code_verifier and its client secret to Google's token endpoint, server to server.
- Google returns an ID token. Your backend verifies it (signature, iss, aud, exp), finds or creates the user by the sub claim, and starts its own session.
https://accounts.google.com/o/oauth2/v2/auth
?response_type=code
&client_id=1234.apps.googleusercontent.com
&redirect_uri=https://myshop.in/auth/google/callback
&scope=openid%20email%20profile
&state=Zx81kq...
&code_challenge=E9Melhoa2Owv...
&code_challenge_method=S256OTP login
An OTP (one-time password) is a short code, usually 6 digits, sent by SMS, WhatsApp or email. Many Indian apps let you log in with just a mobile number and an OTP. In India, business SMS must go through a DLT-registered sender and template (a TRAI rule), so teams usually use a provider such as MSG91, Twilio or Firebase Authentication.
- Random: made with a secure random generator (crypto.getRandomValues or crypto.randomInt), never Math.random.
- Short life: expires in about 5 minutes.
- Limited attempts: after 3 to 5 wrong tries, the code is dead and a new one is needed.
- Stored hashed: keep only a hash of the OTP, like a password, and delete it after use.
- Rate limited: for example one SMS per 30 seconds and a few per hour per number, to stop abuse and SMS bills.
// Example only: runs on a server with Node.js
import { randomInt, createHmac } from 'node:crypto';
const otp = String(randomInt(0, 1000000)).padStart(6, '0'); // '000000' to '999999'
const otpHash = createHmac('sha256', process.env.OTP_SECRET).update(phone + ':' + otp).digest('hex');
await db.otps.insert({ phone, otpHash, expiresAt: Date.now() + 5 * 60 * 1000, attempts: 0 });
await sms.send(phone, 'Your login code is ' + otp + '. It expires in 5 minutes.');Passkeys: no password at all
A passkey uses WebAuthn, a web standard. The phone or laptop makes a key pair for your site: the private key stays on the device, unlocked by fingerprint, face or screen lock, and the server stores only the public key. At login the device signs a challenge from the server. Passkeys cannot be phished, because the browser only uses them on the real domain.
| Method | Strength | Weak point |
|---|---|---|
| Password + hash | Familiar to everyone | Reused and phished passwords |
| SMS OTP | Easy, no password to remember | SIM swap, SMS cost, delays |
| Login with Google | No password stored by you | Depends on another company |
| Passkey | Phishing-proof, fast | Users still learning it; needs a fallback |
Watch the lesson
Login with Google, OTP and passkeys · 1/6
Not every login needs a password. Today: Login with Google, OTP and passkeys.
Prefer a video by a teacher? Find free videos on “oauth 2.0 and openid connect explained simply” on YouTube ↗
In real life: Logging in to a grocery app with your mobile number
A quick-commerce grocery app asks only for your mobile number. The backend checks the rate limit, makes a 6-digit code with a secure random generator, saves its hash with a 5-minute expiry, and sends it through a DLT-registered SMS template. You type the code; the backend compares hashes, counts the attempt, deletes the OTP after success and starts a session. Typing a wrong code five times blocks that code, so nobody can guess all million combinations.
Practice (0/2 done)
Write generateOtp() that returns a random 6-digit string such as "042917" (leading zeros kept). Use crypto.getRandomValues, not Math.random. Tip: take a random 32-bit number and use % 1000000, then padStart(6, "0").
Write verifyOtp(record, code, now). record is { code, expiresAt, attempts }. Check in this order: if record.attempts >= 5 return "locked"; if now > record.expiresAt return "expired"; if code is wrong, add 1 to record.attempts and return "wrong"; otherwise return "ok". (Real apps store a hash of the OTP; here it is plain to keep the task small.)
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.