
Wallet / Auth·EVM · Solana · Multi-chain
Magic
Email magic-link and OAuth login that provisions a non-custodial HSM-backed wallet for each user. Supports EVM, Solana, Flow, and other chain extensions through a single SDK.
- 01passwordless email login
- 02consumer onboarding
- 03embedded wallets without seed phrases
- 04multi-chain user wallets
- 05SSO-style web3 auth
- pnpm add magic-sdk
- pnpm add @magic-ext/oauth @magic-ext/solana
| Variable | Scope | Description |
|---|---|---|
| NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY | Client | Magic publishable API key (pk_live_* / pk_test_*) used in the browser to initialize the SDK. |
| MAGIC_SECRET_KEY | Server | Magic secret API key (sk_live_* / sk_test_*) used server-side to validate DID tokens and call admin APIs. |
Use Magic for email-link auth and embedded wallets. On the client, instantiate `const magic = new Magic(process.env.NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY!, { extensions: [new OAuthExtension()] })` and trigger login with `magic.auth.loginWithEmailOTP({ email })` or `magic.oauth.loginWithRedirect({ provider })`. Read user state via `magic.user.isLoggedIn()` and `magic.user.getInfo()`; sign EVM transactions through `magic.rpcProvider` (wrap with viem/ethers). On the server, verify the DID token via `@magic-sdk/admin` with `magic.token.validate(didToken)` before trusting any user identity. Use `pk_test_`/`sk_test_` keys for testnets, `pk_live_`/`sk_live_` for mainnet.
- ⚑Test and live keys produce different wallets — a user that signs up with a test key has a separate address from their live-key wallet. Never mix.
- ⚑Always verify the DID token server-side with `magic.token.validate()`; the client-supplied address can be spoofed otherwise.
- ⚑Email OTP and magic-link flows are blocked in many in-app browsers (Twitter, Instagram). Detect webviews and fall back to OAuth or copy-paste OTP UI.
- ⚑Per-chain wallets use different extensions (`@magic-ext/solana`, `@magic-ext/flow`, etc.) — installing the wrong extension or mixing RPC URLs leads to silent signing failures.
- ⚑DID tokens default to a 15-minute lifetime; long-lived sessions need a backend-issued JWT layered on top of Magic, not raw DID re-validation.