Most auth tutorials focus on how authentication works such as how to drop in a component, spin up a dev server, and get a login screen running. There's no shortage of guides that tell you which method to use for your use case. What's missing is the hands-on part: actually experiencing each flow the way your users do, so you can feel the friction, see the session it produces, and make an informed decision from the ground up.
Magic link or passkey? Social login or OTP? The answer changes depending on whether you're building a consumer app, a fintech product, a B2B SaaS, or an internal tool. The choice is a product decision that affects activation, security posture, compliance, and long-term maintainability.
To tackle this dilemma, I built an interactive demo called Auth Decision Kit that lets you try three Descope auth flows live: magic link, social login, and passkey. This demo focuses on how each approach fits different product contexts and the tradeoffs you need to consider.
Demo: auth-decision-kit.vercel.app
GitHub: github.com/carasjung/auth-decision-kit
Each method has five tabs:
01 Auth Flow
Authenticate for real using a live Descope integration. See the actual UX users experience.
02 Session Inspector
After authenticating, inspect every claim in your JWT payload. Each field is annotated with what it means, why it matters, and when you'd use it.
03 Decision Matrix
Green / yellow / red ratings across six product contexts: B2B, consumer app, developer tool, internal tool, fintech, SaaS, and mobile-first.
04 Failure Simulator
Trigger each failure mode and see the Descope error code and the correct handling code.
05 Code
Copy-ready implementation snippets for Next.js
The Session Inspector: JWT Breakdown
One of the most useful things I learned building this is how different the JWT payload looks depending on which auth method you used and why they matter for your backend logic.
After a magic link auth, your session contains authenticationMethod: "magiclink" and verifiedEmail: true. The email verification is implicit, clicking the link is proof of inbox access. This is a meaningful signal for risk scoring and it also shows that magic link is a single factor (access to your inbox). For products that require two factors like healthcare and fintech, magic link on its own won’t satisfy.
After social login, you get the provider's access token nested under oauth.google.accessToken (or whichever provider). You also get externalIds.google, a stable provider-specific user ID that won't change even if the user changes their email address on Google's side. That's the field you want for account linking. Since you get free profile data, this is effective for consumer and developer tools.
After a passkey auth, the amr (Authentication Methods References) claim contains "hwk" (hardware key) and "user". This is the claim compliance teams care about. It's proof that a hardware-bound credential was used, not just a password or a link. Passkey is also the only method here where the private key never leaves the user’s device. Even a full Descope breach couldn’t expose user credentials.
The Decision Matrix
Here's a condensed version of what I found after thinking through six product contexts for each method:
Magic link is the sweet spot for B2B SaaS and early-stage products. Zero password management, implicit email verification, and simple implementation. However, it falls apart on mobile (context switch to email app kills conversion) and in high-security contexts where email as a sole factor isn't enough.
Social login is the fastest path to activation for consumer and developer tools. GitHub login in particular gives you free org and repo data via the OAuth token, which is useful for developer-focused products. Avoid it for fintech and banking where regulations often require you to own the identity directly.
Passkey is genuinely the best option for mobile-first and high-security context. Phishing-proof by design, the private key never leaves the device. The catch: users still need education on what a passkey is and you need a fallback for older browsers and lost devices.
Most products should offer at least two methods where one can be the default while the other an alternative. For instance, using magic link as the default and passkey as the upgrade path once users are comfortable.
The Failure Simulator
Auth flows break in predictable ways. Understanding those failure points from day one lets you design a seamless recovery experience so users can continue without friction and avoid escalating to support.
The failure simulator surfaces these scenarios using real Descope error codes and responses. While it doesn’t make live network calls, it replays actual API error outputs so you can explore failure cases without having to intentionally break a real session.
Magic links expire (Descope's default is 2 minutes). When they do, the onError callback fires with error code E011303. Your UI should catch this and offer to send a new link, not show a generic error message.
Social login gets cancelled. Users click "Continue with Google," see the permissions screen, and hit Cancel. That fires E062503. The right response is to return the user silently to the login screen and treat a cancellation as a choice, not an error.
Passkeys on new devices fire E083002 (WebAuthn NotAllowedError). The recovery flow is: fall back to magic link or OTP to verify identity, then offer to enroll a passkey on the new device. This is also why you should never make passkey the only auth method since you always need a fallback for device loss.
Stack and Setup
- Next.js 15 with App Router
-
Descope Next.js SDK (
@descope/nextjs-sdk) for auth flows and session management - Framer Motion for tab transitions
- Tailwind CSS for layout
The entire setup is about 800 lines of TypeScript across nine files. All core data (steps, session highlights, decision matrix scores, failure scenarios, and code snippets) lives in a single lib/auth.ts file. Adding a new auth method requires only a single entry point, keeping the system easy to extend.
To run it yourself:
git clone https://github.com/carasjung/auth-decision-kit
cd auth-decision-kit
npm install
cp .env.local.example .env.local
# add your NEXT_PUBLIC_DESCOPE_PROJECT_ID
npm run dev
You'll need a free Descope account. Once you’ve created your account, grab your Project ID from app.descope.com/settings/project and configure a sign-up-or-in flow with whichever methods you want to test.
From Demo to Decision
There are plenty of great auth demos that show how things work. This one focuses on how to choose between them.
Auth is infrastructure and like many infrastructure decisions, the cost of getting it wrong rarely shows up immediately. It appears later through conversion drop-offs, security tradeoffs, compliance constraints, and migrations.
While modern tools make it easier to support multiple methods and evolve your approach over time, the decision of what to use and when still requires good judgement upfront. This project is designed to help make that choice more intentional.
The live demo is at auth-decision-kit.vercel.app and the full source is on GitHub.







Top comments (0)