Web SDK Reference
Web SDK Reference
#Overview
The Magic SDK for Javascript is your entry-point to secure, passwordless authentication for your application. This guide will cover all client-side methods, within each module. It will help you get familiarized with the JavaScript SDK.
#Getting Started
The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic publishable key, which you can get by signing up for a developer account.
#Installation
To use Magic in your application, install the magic-sdk
dependency.
01npm install magic-sdk
#Constructor
#Magic()
Configure and construct your Magic SDK instance.
Parameter | Type | Definition |
| String | Your publishable API key retrieved from the Magic Dashboard. |
| String | Customize the language of Magic's modal, email and confirmation screen. See Localization for more. |
| Boolean | Enable |
| String | Object | (String): A representation of the connected Ethereum network (mainnet or goerli). (Object): A custom Ethereum Node configuration with the following shape: |
| String | A URL pointing to the Magic |
| Boolean | An optional flag to delay the loading of the Magic Iframe's static assets until an SDK function is explicitly invoked. Set this to true if latency bottlenecks are a concern. |
| Boolean | An optional flag to allow the usage of the local storage as cache. Currently it is only used for faster calls to |
#Initialization
01import { Magic } from 'magic-sdk';
02
03let magic;
04
05// Construct with an API key:
06// network configuration defaults to 'mainnet' (Magic's RPC)
07magic = new Magic('PUBLISHABLE_API_KEY');
08
09// Construct with an API key and use Magic's RPC:
10magic = new Magic('PUBLISHABLE_API_KEY', {
11 network: 'sepolia' // or 'mainnet'
12});
13
14// Construct with an API key and set custom network:
15const customNodeOptions = {
16 rpcUrl: 'https://polygon-rpc.com', // your rpc URL
17 chainId: 137 // corresponding chainId for your rpc url
18};
19magic = new Magic('PUBLISHABLE_API_KEY', {
20 network: customNodeOptions // connected to Polygon Mainnet
21});
22
23// Construct with an API key and optional params
24magic = new Magic('PUBLISHABLE_API_KEY', {
25 locale: 'es',
26 testMode: true,
27 deferPreload: true,
28 endpoint: '...',
29});
#Global Methods
Global methods and properties are accessible on the Magic SDK instance itself.
#preload
Starts downloading the static assets required to render the Magic iframe context.
As of magic-sdk version 21.0.0, the SDK constructor will preload the iframe's static assets by default, unless the deferPreload
flag is passed into the constructor options. See Release Notes
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY', { deferPreload: true });
04
05// ...
06
07magic.preload;
#Arguments
- None
#Returns
Promise<void>
: A Promise that resolves to indicate the<iframe>
is ready for requests
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05magic.preload().then(() => console.log('Magic <iframe> loaded.'));
#Auth Module
The Auth Module and it's members are accessible on the Magic SDK instance by the auth
property.
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05magic.auth;
06magic.auth.loginWithMagicLink;
07magic.auth.loginWithEmailOTP;
08magic.auth.loginWithSMS;
09magic.auth.loginWithCredential;
10magic.auth.updateEmailWithUI;
#loginWithMagicLink
Authenticate a user passwordlessly using a "magic link" sent to the specified user's email address.
Only available with Dedicated Wallet.
#Arguments
email
(String): The user email to log in withlifespan?
(Number): Set the lifespan of the resolved Decentralize ID token. Defaults to 900s (15 mins)showUI?
(Boolean): Iftrue
, show an out-of-the-box UI to accept the OTP from user. Defaults totrue
redirectURI?
(String): You can provide a redirect URI that Magic will point to after the user clicks their email link. If included, don't forget to callloginWithCredential
at the specified redirect location
Note: If you are securing a resource server and have your own signup flow after this call resolves, be mindful of where you're calling signup in your implementation to avoid potential concurrency issues!
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// log in a user by their email
06try {
07 await magic.auth.loginWithMagicLink({ email: 'hello@example.com' });
08} catch {
09 // Handle errors if required!
10}
11
12// log in a user by their email, without showing an out-of-the box UI.
13try {
14 await magic.auth.loginWithMagicLink({ email: 'hello@example.com', showUI: false });
15} catch {
16 // Handle errors if required!
17}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.auth.loginWithMagicLink({ email: 'hello@example.com', showUI: false });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.MagicLinkFailedVerification:
11 case RPCErrorCode.MagicLinkExpired:
12 case RPCErrorCode.MagicLinkRateLimited:
13 case RPCErrorCode.UserAlreadyLoggedIn:
14 // Handle errors accordingly :)
15 break;
16 }
17 }
18}
#Events
Event Name | Definition |
email-not-deliverable | Dispatched if the magic link email is unable to be delivered. |
email-sent | Dispatched when the magic link email has been successfully sent from the Magic Link server. |
retry | Dispatched when the user restarts the flow. This can only happen if showUI: true . |
#loginWithEmailOTP
Authenticate a user passwordlessly using an email one-time code sent to the specified user's email address.
Only available with Dedicated Wallet.
#Arguments
email
(String): The user email to log in withlifespan?
(Number): Set the lifespan of the resolved Decentralize ID token. Defaults to 900s (15 mins)showUI?
(Boolean): Iftrue
, show an out-of-the-box UI to accept the OTP from user. Defaults totrue
deviceCheckUI?
(Boolean): The default value istrue
. It shows Magic branded UI securing sign-ins from new devices. If set tofalse
, the UI will remain hidden. However, this the false value only takes effect when you have also set theshowUI: false
. If you enable Device Verification in the Magic dashboard and are passingshowUI: false
you must also explicitly passdeviceCheckUI: false
Available from magic-sdk@19.1.0
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// log in a user by their email
06try {
07 await magic.auth.loginWithEmailOTP({ email: 'hello@example.com' });
08} catch {
09 // Handle errors if required!
10}
11
12// log in a user by their email, without showing an out-of-the box UI.
13try {
14 await magic.auth.loginWithEmailOTP({ email: 'hello@example.com', showUI: false });
15} catch {
16 // Handle errors if required!
17}
#Event Handling
A white-label OTP login flow is available when passing showUI: false
to this login method. Here's a short example to illustrate listening for and emitting events during the login flow:
01import {
02 Magic,
03 LoginWithEmailOTPEventOnReceived,
04 LoginWithEmailOTPEventEmit,
05 RecencyCheckEventOnReceived,
06 RecencyCheckEventEmit,
07 DeviceVerificationEventEmit,
08} from 'magic-sdk';
09
10const magic = new Magic('PUBLISHABLE_API_KEY');
11
12try {
13 // Initiate login flow
14 const handle = magic.auth.loginWithEmailOTP({ email: "hello@example.com", showUI: false, deviceCheckUI: false });
15
16 handle
17 .on(LoginWithEmailOTPEventOnReceived.EmailOTPSent, () => {
18 // The email has been sent to the user
19
20 // Prompt the user for the OTP
21 const otp = window.prompt('Enter Email OTP');
22
23 // Send the OTP for verification
24 handle.emit(LoginWithEmailOTPEventEmit.VerifyEmailOtp, otp);
25 })
26 .on(LoginWithEmailOTPEventOnReceived.InvalidEmailOtp, () => {
27 // User entered invalid OTP
28
29 // Have the user retry entering the OTP and emit via VerifyEmailOtp.
30 // You can limit retries and emit Cancel to cancel login flow.
31
32 // cancel login request
33 handle.emit(LoginWithEmailOTPEventEmit.Cancel);
34 })
35 .on('done', (result) => {
36 // is called when the Promise resolves
37
38 // convey login success to user
39 alert('Login complete!');
40
41 // DID Token returned in result
42 const didToken = result;
43 })
44 .on('error', (reason) => {
45 // is called if the Promise rejects
46 console.error(reason);
47 })
48 .on('settled', () => {
49 // is called when the Promise either resolves or rejects
50 })
51
52 //** MFA Verification Events (if enabled for app)
53
54 .on(LoginWithEmailOTPEventOnReceived.MfaSentHandle, () => {
55 // Prompt the user for the MFA TOTP
56 const mfa_totp = window.prompt('Enter MFA TOTP');
57
58 // Send the MFA TOTP for verification
59 handle.emit(LoginWithEmailOTPEventEmit.VerifyMfaCode, mfa_totp)
60 })
61 .on(LoginWithEmailOTPEventOnReceived.InvalidMfaOtp, () => {
62 // User entered invalid OTP
63
64 // Have the user retry entering the MFA OTP and emit via VerifyMfaCode.
65 // You can limit retries and emit Cancel to cancel login flow.
66
67 // cancel login request
68 handle.emit(LoginWithEmailOTPEventEmit.Cancel);
69 })
70
71 //** Device Verification Events (if enabled for app)
72
73 .on(DeviceVerificationEventOnReceived.DeviceNeedsApproval, () => {
74 // is called when device is not recognized and requires approval
75 })
76 .on(DeviceVerificationEventOnReceived.DeviceVerificationEmailSent, () => {
77 // is called when the device verification email is sent
78 })
79 .on(DeviceVerificationEventOnReceived.DeviceApproved, () => {
80 // is called when the device has been approved
81 })
82 .on(DeviceVerificationEventOnReceived.DeviceVerificationLinkExpired, () => {
83 // is called when the device verification link is expired
84
85 // Retry device verification
86 handle.emit(DeviceVerificationEventEmit.Retry);
87 });
88
89 // LoginWithEmailOTPEventEmit.Cancel can always be emitted to terminate the unresolved request.
90
91} catch (err) {
92 // handle errors
93}
#Event Enums
Email OTP
Event Name | Definition |
email-otp-sent | Dispatched when the OTP email has been successfully sent from the Magic server. |
verify-email-otp | Emit along with the OTP to verify the code from user. |
invalid-email-otp | Dispatched when the OTP sent fails verification. |
| Emit to cancel the login request. |
Device Verification
Event Name | Definition |
device-needs-approval | Dispatched when the device is unrecognized and requires user approval |
device-verification-email-sent | Dispatched when the device verification email is sent |
device-approved | Dispatched when the user has approved the unrecongized device |
| Dispatched when the email verification email has expired |
| Emit to restart the device registration flow |
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.auth.loginWithEmailOTP({ email: 'hello@example.com' });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.MagicLinkExpired
11 case RPCErrorCode.UserAlreadyLoggedIn:
12 // Handle errors accordingly :)
13 break;
14 }
15 }
16}
#loginWithSMS
Authenticate a user passwordlessly using a one-time code sent to the specified phone number.
List of Currently Blocked Country Codes
Only available with Dedicated Wallet.
#Arguments
phoneNumber
(String): E.164 formatted phone numberlifespan?
(Number): Set the lifespan of the resolved Decentralize ID token. Defaults to 900s (15 mins)
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// log in a user by their phone number
06try {
07 await magic.auth.loginWithSMS({ '+14151231234' });
08} catch {
09 // Handle errors if required!
10}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.auth.loginWithSMS({ phoneNumber: "+14151231234" });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.AccessDeniedToUser:
11 case RPCErrorCode.MagicLinkRateLimited:
12 case RPCErrorCode.UserAlreadyLoggedIn:
13 // Handle errors accordingly :)
14 break;
15 }
16 }
17}
#loginWithCredential
Authenticate a user via a "Magic Credential," a special, one-time-use DID Token created by the user to hydrate their authentication between page reloads. For example: when executing the loginWithMagicLink
flow with a redirectURI
specified, you can invoke this method to complete the authentication "callback," similar in principal to OAuth 2.0 flows.
If given no arguments, this method will parse the credential token automatically from window.location.search
.
Only available with Dedicated Wallet.
#Arguments
credentialOrQueryString
(String): A credential token or a valid query string (prefixed with?
or#
). By default, this method will look for the a credential token on themagic_credential
key ofwindow.location.search
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
From your login page:
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.auth.loginWithMagicLink({
07 email: 'hello@example.com',
08 redirectURI: 'https://yourdomain.com/your/magic/link/callback',
09 });
10
11 // When the user clicks their magic link, they will be logged-in here
12 // and in the "callback" context.
13} catch {
14 // Handle errors if required!
15}
From your authentication callback page:
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.auth.loginWithCredential();
07} catch {
08 // Handle errors if required!
09}
10
11// You can also provide the credential yourself
12try {
13 await magic.auth.loginWithCredential('iamacredentialtoken');
14} catch {
15 // Handle errors if required!
16}
17
18// You can also provide the credential as a query string
19try {
20 await magic.auth.loginWithCredential(window.location.search);
21} catch {
22 // Handle errors if required!
23}
#updateEmailWithUI
Initiates the update email flow that allows a user to change their email address.
Only available with Dedicated Wallet.
#Arguments
email
(String): The new email to update toshowUI?
(Boolean): Iftrue
, shows an out-of-the-box pending UI which includes instructions on which step of the confirmation process the user is on. Dismisses automatically when the process is complete
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if update email is successful and rejects with a specific error code if the request fails
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('API_KEY');
04
05// Initiates the flow to update a user's current email to a new one.
06try {
07 ...
08 /* Assuming user is logged in */
09 await magic.auth.updateEmailWithUI({ email: 'new_user_email@example.com' });
10} catch {
11 // Handle errors if required!
12}
13
14/**
15 * Initiates the flow to update a user's current email to a new one,
16 * without showing an out-of-the box UI.
17 */
18try {
19 /* Assuming user is logged in */
20 await magic.auth.updateEmailWithUI({ email: 'new_user_email@example.com', showUI: false });
21} catch {
22 // Handle errors if required!
23}
#Event Handling
A white-label update email flow is available when passing showUI: false
to this method. The white-label flow is only supported in Magic SDK v22.0.0 and above. Here's a short example to illustrate listening for and emitting events during the flow:
01import {
02 Magic,
03 RecencyCheckEventOnReceived,
04 RecencyCheckEventEmit,
05 UpdateEmailEventOnReceived,
06 UpdateEmailEventEmit,
07} from 'magic-sdk';
08
09const magic = new Magic('API_KEY');
10
11try {
12 /* Initiates update email flow to update a user's current email to a new one */
13 const handle = await magic.auth.updateEmailWithUI({
14 email: 'new_user_email@example.com',
15 showUI: false,
16 });
17
18 /*
19 Recency Check Events & Emit
20 */
21
22 let recencyCheckRetries = 5;
23
24 handle
25 .on(RecencyCheckEventOnReceived.EmailSent, () => {
26 // Email OTP has been sent to the user's primary email
27
28 // Prompt the user for the OTP
29 const otp = window.prompt('Primary Email OTP');
30
31 // Send the OTP for verification
32 handle.emit(RecencyCheckEventEmit.VerifyEmailOtp, otp);
33 })
34 .on(RecencyCheckEventOnReceived.PrimaryAuthFactorVerified, () => {
35 window.alert('Primary Factor has been verified');
36 })
37 .on(RecencyCheckEventOnReceived.EmailNotDeliverable, () => {
38 // Email OTP was undeliverable to user's primary email
39
40 // Cancel update email request
41 handle.emit(RecencyCheckEventEmit.Cancel);
42 window.alert('Email Not Deliverable');
43 })
44 .on(RecencyCheckEventOnReceived.EmailExpired, () => {
45 // User entered expired OTP
46
47 handle.emit(RecencyCheckEventEmit.Cancel);
48 window.alert('Expired OTP');
49 })
50 .on(RecencyCheckEventOnReceived.InvalidEmailOtp, () => {
51 // User entered invalid OTP; you may limit retries and cancel the request
52
53 if (!recencyCheckRetries) {
54 // Cancel update email request
55 alert('Too many attempts');
56 handle.emit(RecencyCheckEventEmit.Cancel);
57 } else {
58 const otp = window.prompt(
59 `Invalid code, Please enter OTP again. Retries left: ${recencyCheckRetries}`,
60 );
61 recencyCheckRetries--;
62
63 // Send the OTP for verification
64 handle.emit(RecencyCheckEventEmit.VerifyEmailOtp, otp);
65 }
66 });
67
68 /*
69 Update Email Events & Emit
70 */
71
72 let updateEmailRetries = 5;
73
74 handle
75 .on(UpdateEmailEventOnReceived.EmailSent, () => {
76 // Email OTP has been sent to the user's secondary email
77
78 // Prompt the user for the OTP
79 const otp = window.prompt('Enter new Email OTP');
80
81 // Send the OTP for verification
82 handle.emit(UpdateEmailEventEmit.VerifyEmailOtp, otp);
83 })
84 .on(UpdateEmailEventOnReceived.InvalidEmail, () => {
85 // Email OTP was undeliverable to user's secondary email
86
87 const newEmail = window.prompt('Invalid Email, Enter a new Email');
88
89 // Try same or new email address
90 handle.emit(
91 UpdateEmailEventEmit.RetryWithNewEmail,
92 newEmail || email,
93 );
94 })
95 .on(UpdateEmailEventOnReceived.EmailAlreadyExists, () => {
96 // Account already exists for new email address
97
98 const newEmail = window.prompt('Email address already in use, Enter a different Email');
99
100 // Try same or new email address
101 handle.emit(
102 UpdateEmailEventEmit.RetryWithNewEmail,
103 newEmail || email,
104 );
105 })
106 .on(UpdateEmailEventOnReceived.InvalidEmailOtp, () => {
107 // User entered invalid OTP; you may limit retries and cancel the request
108
109 if (!updateEmailRetries) {
110 // Cancel update email request
111 alert('Too many attempts');
112 handle.emit(UpdateEmailEventEmit.Cancel);
113 } else {
114 const otp = window.prompt(
115 `Invalid code, Please enter OTP again. Retries left: ${updateEmailRetries}`,
116 );
117 updateEmailRetries--;
118
119 // Send the OTP for verification
120 handle.emit(UpdateEmailEventEmit.VerifyEmailOtp, otp);
121 }
122 })
123 .on(UpdateEmailEventOnReceived.EmailUpdated, () => {
124 // Update email succcessful
125
126 alert('Email Updated');
127 })
128
129 handle
130 .on('error', () => {
131 // is called if the Promise rejects
132
133 alert('Error occurred');
134 });
135
136 const res = await handle;
137 console.log(res);
138
139 // Can also handle successful email update here
140 alert('Email Updated');
141} catch {
142 // Handle errors if required!
143}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const magic = new Magic('API_KEY');
04
05try {
06 await magic.auth.updateEmailWithUI({ email: 'hello@example.com', showUI: false });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.UpdateEmailFailed:
11 // Handle errors accordingly :)
12 break;
13 }
14 }
15}
#Wallet Module
The Wallet Module and it's members are accessible on the Magic SDK instance by the wallet
property.
Note: The Wallet Module is currently only compatible with Ethereum, Polygon, Base, Arbitrum, Optimism, and Flow (no NFTs).
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05magic.wallet;
06magic.wallet.connectWithUI;
07magic.wallet.showUI;
08magic.wallet.showAddress;
09magic.wallet.showBalances;
10magic.wallet.showNFTs;
11magic.wallet.showSendTokensUI;
12magic.wallet.showOnRamp; // enterprise only
13magic.wallet.getProvider;
#connectWithUI
Renders a simple login form UI to collect the user's email address and authenticate them passwordlessly using a one-time passcode (OTP) sent to their email address they input.
#Arguments
- None
#Returns
- A
promiEvent
which returns anString[]
when resolved: An array of user accounts that are connected, with the first element being the current public address of the user. You can read more on PromiEvents here.
#Example
01import { Magic } from "magic-sdk"
02
03const accounts = await magic.wallet.connectWithUI();
04
05/* Optionally, chain to the id token creation event if needed and configured (Universal Wallets only) */
06magic.wallet.connectWithUI().on('id-token-created', (params) => {
07 const { idToken } = params
08 console.log(idToken)
09 // send to your resource server for validation
10 // ...
11});
#Events
The id-token-created event is only emitted for Universal Wallet apps. To generate a DID token for a Dedicated Wallet app, please use getIdToken.
Event Name | Definition |
| Returns an object containing a short lived, time bound ID token that can be used to verify the ownership of a user's wallet address on login. Read more about this token and how to use it. |
#showUI
Displays the fully navigable wallet to the user that adheres to the toggled configurations on your developer dashboard’s Widget UI tab. This is only supported for users who login with email or Google and not third party wallets such as metamask. User must be signed in for this method to return or else it will throw an error.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
Optionally, add a .on()
handler to catch the disconnect
event emitted when the user logs out from the wallet widget.
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showUI()
#showAddress
Displays an iframe with the current user’s wallet address in a QR Code.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showAddress()
#showBalances
Displays an iframe that displays the user’s token balances from the currently connected network.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showBalances()
#showNFTs
Displays an iframe that shows the user’s NFTs in both an aggregated and detailed individual view. Supported only on Ethereum and Polygon. Ensure this is enabled in your developer dashboard via the ‘Widget UI’ tab.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showNFTs()
#showSendTokensUI
Displays an iframe with UI to help the user transfer tokens from their account to another address.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showSendTokensUI()
#showOnRamp
Displays an iframe modal with various on ramp providers for the user to purchase crypto from directly to their wallet.
To use the fiat on ramp for Dedicated Wallet apps, you will need to contact us to KYB with the payment provider prior to use. Once approved, ensure this toggle is enabled in your developer dashboard via the ‘Widget UI’ tab.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05await magic.wallet.showOnRamp()
#getProvider
This method is introduced in magic-sdk@17.0.0
and must be used to get the current provider if third party wallets are enabled. If not using third party wallets, we suggest using magic.rpcProvider
.
#Arguments
- None
#Returns
Object
: The rpc provider of the wallet a user is currently logged in with (MetaMask, Coinbase Wallet or Magic)
Important: To ensure rpc requests are routed to the correct wallet, developers must re-fetch the provider object using getProvider()
and re-initialize the web3.js or ethers.js instance any time a user logs in, logs out, or disconnects their wallet.
#Web3.js/Ethers.js
01import { Magic } from "magic-sdk"
02import Web3 from 'web3';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 network: "goerli",
06});
07
08const provider = await magic.wallet.getProvider();
09
10const web3 = new Web3(provider);
11
12const accounts = await magic.wallet.connectWithUI();
13
14// Listen for events
15web3.currentProvider.on('accountsChanged', handleAccountsChanged);
16web3.currentProvider.on('chainChanged', handleChainChanged);
#User Module
The User Module and it's members are accessible on the Magic SDK instance by the user
property.
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05magic.user;
06magic.user.getIdToken;
07magic.user.generateIdToken;
08magic.user.getInfo;
09magic.user.isLoggedIn;
10magic.user.logout;
11magic.user.showSettings;
12magic.user.enableMFA;
13magic.user.disableMFA;
14magic.user.revealPrivateKey;
15magic.user.requestInfoWithUI;
#getIdToken
Generates a Decentralized Id Token which acts as a proof of authentication to resource servers.
Only available with Dedicated Wallet.
#Arguments
lifespan?
(Number): Will set the lifespan of the generated token. Defaults to 900s (15 mins)
#Returns
PromiEvent<string>
: Base64-encoded string representation of a JSON tuple representing[proof, claim]
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const idToken = await magic.user.getIdToken();
08} catch {
09 // Handle errors if required!
10}
#generateIdToken
Generates a Decentralized ID token with optional serialized data.
Only available with Dedicated Wallet.
#Arguments
lifespan?
(Number): Will set the lifespan of the generated token. Defaults to 900s (15 mins)attachment?
(String): Will set a signature of serialized data in the generated token. Defaults to"none"
#Returns
PromiEvent<string>
: Base64-encoded string representation of a JSON tuple representing[proof, claim]
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const idToken = await magic.user.generateIdToken({ attachment: 'SERVER_SECRET' });
08} catch {
09 // Handle errors if required!
10}
#getInfo
Retrieves information for the authenticated user.
#Arguments
- None
#Returns
PromiEvent<string>
:issuer
(String): The Decentralized ID of the user. In server-side use-cases, we recommend this value to be used as the user ID in your own tables.email
(String): Email address of the authenticated userphoneNumber
(String): The phone number of the authenticated userpublicAddress
(String): The authenticated user's public address (a.k.a.: public key)isMfaEnabled
(Boolean): Whether or not multi-factor authentication is enabled for the userrecoveryFactors
(Array): Any recovery methods that have been enabled (ex.[{ type: 'phone_number', value: '+99999999' }]
)
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const userInfo = await magic.user.getInfo();
08} catch {
09 // Handle errors if required!
10}
#isLoggedIn
Checks if a user is currently logged in to the Magic SDK.
#Arguments
- None
#Returns
PromiEvent<boolean>
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 const isLoggedIn = await magic.user.isLoggedIn();
07 console.log(isLoggedIn); // => `true` or `false`
08} catch {
09 // Handle errors if required!
10}
#logout
Logs out the currently authenticated Magic user
#Arguments
- None
#Returns
PromiEvent<boolean>
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.user.logout();
07 console.log(await magic.user.isLoggedIn()); // => `false`
08} catch {
09 // Handle errors if required!
10}
#showSettings
Displays an iframe with the current user’s settings. Allows for users to update their email address, enable multi-factor authentication, and add a recovery factor.
Only available with Dedicated Wallet. Access to MFA and account recovery require paid add-ons.
#Arguments
page?
(String): Optional argument to deeplink to a specific page ('mfa' | 'update-email' | 'recovery'
)
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.user.showSettings();
07} catch {
08 // Handle errors if required!
09}
10
11// Deeplink to MFA view
12try {
13 await magic.user.showSettings({ page: 'mfa' });
14} catch {
15 // Handle errors if required!
16}
#showSettings({ showUI: false, page: 'recovery' })
Whitelabel the flow starting from where the user needs to add a recovery factor phone number. This allows you to implement your own UI, but you will need to implement some custom event handling according to your needs.
Only available with Dedicated Wallet. Access to account recovery require paid add-ons.
#Arguments
page
(String):'recovery'
showUI
(Boolean):false
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 let handle = magic.user.showSettings({ showUI: false, page: 'recovery' });
07
08 handle.emit(RecoveryFactorEventEmit.StartEditPhoneNumber);
09 // Handle start of adding or editing recovery factor phone number
10
11 handle.on(
12 RecencyCheckEventOnReceived.PrimaryAuthFactorNeedsVerification, () => {
13 // Event triggered if primary auth factor (email) needs verification
14 },
15 );
16
17 handle.on(RecencyCheckEventOnReceived.EmailSent, () => {
18 // Send OTP to email if primary auth factor needs verification
19 // Prompt for OTP and emit it to verify primary auth factor
20
21 const code = window.prompt(Please enter the code which was sent to your email');
22 handle.emit(RecencyCheckEventEmit.VerifyEmailOtp, code);
23 });
24
25 handle.on(RecencyCheckEventOnReceived.PrimaryAuthFactorVerified, () => {
26 // Event triggered if email verification success
27 });
28
29 handle.on(RecoveryFactorEventOnReceived.EnterNewPhoneNumber, () => {
30 // Prompt for secondary factor (phone number) and emit it to send SMS OTP
31
32 const phoneNumber = window.prompt('Enter a phone number');
33 handle.emit(RecoveryFactorEventEmit.SendNewPhoneNumber, phoneNumber);
34 });
35
36 handle.on(RecoveryFactorEventOnReceived.EnterOtpCode, () => {
37 // Prompt for SMS OTP and emit it to verify secondary auth factor
38
39 const otp = window.prompt('Enter SMS OTP');
40 handle.emit(RecoveryFactorEventEmit.SendOtpCode, otp);
41 });
42
43 handle.on('done', () => {
44 // Successfully added secondary recovery factor
45 });
46
47 handle.on(RecoveryFactorEventOnReceived.MalformedPhoneNumber, () => {
48 // Event triggered if submitted phone number is invalid
49
50 // Prompt for phone number and emit it to send SMS OTP
51 // Add your own retry logic to limit retries
52 const phoneNumber = window.prompt(
53 'You entered an invalid phone number, please try again'
54 );
55 handle.emit(RecoveryFactorEventEmit.SendNewPhoneNumber, phoneNumber);
56 });
57
58 handle.on(RecoveryFactorEventOnReceived.RecoveryFactorAlreadyExists, () => {
59 // Event triggered if user is trying to add a phone number that's already in place
60 });
61
62 handle.on(RecoveryFactorEventOnReceived.InvalidOtpCode, () => {
63 // Event triggered when submitted SMS OTP is invalid
64
65 const code = window.prompt('Invalid OTP code. Please try one more time');
66 handle.emit(RecoveryFactorEventEmit.SendOtpCode, code);
67 });
68} catch {
69 // Handle errors if required!
70}
#recoverAccount()
A user can recover their email account if they've setup a phone number as a recovery factor. Calling this method will display a modal for the user to submit the sent SMS OTP.
Only available with Dedicated Wallet. Access to account recovery require paid add-ons.
#Arguments
email
(String): The email address (primary auth factor) of the user
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if the recovery is successful and rejects if the request fails
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 magic.user.recoverAccount({ email: email })
07} catch {
08 // Handle errors if required!
09}
#enableMFA
Displays an iframe deep linked to the beginning of the enable MFA flow. This is the same as calling showSettings({ page: 'mfa' })
Only available with Dedicated Wallet. Access to MFA require paid add-ons. Available as of magic-sdk v28.11.0
#Arguments
showUI?
(Boolean): Iftrue
, show an out-of-the-box UI to take user through flow to enable MFA. Defaults totrue
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02import {
03 EnableMFAEventEmit,
04 EnableMFAEventOnReceived,
05} from '@magic-sdk/types';
06
07const magic = new Magic('PUBLISHABLE_API_KEY');
08
09// user enables MFA through Magic UI
10try {
11 await magic.user.enableMFA();
12} catch {
13 // Handle errors if required!
14}
15
16// user enables MFA through whitelabel UI
17try {
18 const handle = magic.user.enableMFA({ showUI: false });
19
20 handle
21 .on(EnableMFAEventOnReceived.MFASecretGenerated, ({ QRCode, key }) => {
22 // Display QR code
23 window.alert(`QRCode: ${QRCode}\nKey:${key}`);
24
25 // Prompt for MFA TOTP and emit it to enable MFA
26 const totp = window.prompt('Scan QR code and enter TOTP from MFA app');
27 handle.emit(EnableMFAEventEmit.VerifyMFACode, totp);
28 })
29 .on(EnableMFAEventOnReceived.InvalidMFAOtp, ({ errorCode }) => {
30 // User entered invalid MFA TOTP
31
32 // Have the user retry entering the MFA TOTP and emit via VerifyMFACode.
33 // You may display error and limit retries by emitting Cancel.
34 handle.emit(EnableMFAEventEmit.Cancel);
35 })
36 .on(EnableMFAEventOnReceived.MFARecoveryCodes, ({ recoveryCode }) => {
37 // Enable MFA success, user entered valid MFA TOTP, display Recovery Code
38 window.alert(`MFA enabled! Recovery code - ${recoveryCode}`);
39 })
40 .on('error', (error) => {
41 // Handle enable MFA errors
42 )};
43} catch {
44 // Handle errors if required!
45}
#Events
Event Name | Definition |
mfa-secret-generated | Dispatched when the user starts the enable MFA process. |
invalid-mfa-otp | Dispatched when the MFA TOTP submitted by user is invalid. |
mfa-recovery-codes | Dispatched when the MFA TOTP submitted by user is valid. |
| Emit to submit MFA TOTP submitted by user. |
| Emit to cancel the MFA enable flow. |
#disableMFA
Displays an iframe deep linked to the beginning of the disable MFA flow. This method assumes MFA has been enabled for the logged in user. This cannot be achieved by calling showSettings()
.
Only available with Dedicated Wallet. Access to MFA require paid add-ons. Available as of magic-sdk v28.11.0
#Arguments
showUI?
(Boolean): Iftrue
, show an out-of-the-box UI to take user through flow to disable MFA. Defaults totrue
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02import {
03 DisableMFAEventEmit,
04 DisableMFAEventOnReceived,
05} from '@magic-sdk/types';
06
07const magic = new Magic('PUBLISHABLE_API_KEY');
08
09// user disables MFA through Magic UI
10try {
11 await magic.user.disableMFA();
12} catch {
13 // Handle errors if required!
14}
15
16// user disables MFA through whitelabel UI
17try {
18 const handle = magic.user.disableMFA({ showUI: false });
19
20 handle
21 .on(DisableMFAEventOnReceived.MFACodeRequested, () => {
22 // Prompt user for MFA TOTP and emit it to disable MFA
23 const totp = window.prompt('Submit MFA TOTP');
24 handle.emit(DisableMFAEventEmit.VerifyMFACode, totp);
25
26 // If user lost device and needs to recover with recovery code
27 const recoveryCode = window.prompt('Enter Recovery Code');
28 handle.emit(DisableMFAEventEmit.LostDevice, recoveryCode);
29 })
30 .on(DisableMFAEventOnReceived.InvalidMFAOtp, ({ errorCode }) => {
31 // Handle user entered invalid MFA TOTP
32
33 // Have the user retry entering the MFA TOTP and emit via VerifyMFACode.
34 // You may display error and limit retries by emitting Cancel.
35 handle.emit(DisableMFAEventEmit.Cancel);
36 })
37 .on(DisableMFAEventOnReceived.InvalidRecoveryCode, () => {
38 // Handle user entered invalid Recovery Code
39
40 // Have the user retry entering the Recovery Code and emit via LostDevice.
41 // You may display error and limit retries by emitting Cancel.
42 handle.emit(DisableMFAEventEmit.Cancel);
43 })
44 .on('done', () => {
45 // Handle successful disable MFA
46 })
47 .on('error', (error) => {
48 // Handle disable MFA errors
49 )};
50} catch {
51 // Handle errors if required!
52}
#Events
Event Name | Definition |
mfa-code-requested | Dispatched when the user starts the disable MFA process. |
invalid-mfa-otp | Dispatched when the MFA TOTP submitted by user is invalid. |
invalid-recovery-code | Dispatched when the Recovery Code submitted by user is invalid. |
| Emit to submit MFA TOTP submitted by user. |
| Emit to submit Recovery Code submitted by user. |
| Emit to cancel the MFA disable flow. |
#revealPrivateKey
Displays an iframe revealing the current user’s private key. Allows for users to take their private key to another wallet. Neither Magic nor the developer can see this key; only the end user can.
Only available with Dedicated Wallet.
#Arguments
- None
#Returns
Promise
which resolves when the user closes the window
#Example
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('PUBLISHABLE_API_KEY');
04
05try {
06 await magic.user.revealPrivateKey();
07} catch {
08 // Handle errors if required!
09}
#requestInfoWithUI
Universal wallets will soon be merged with Dedicated Wallets into a single product line. Universal apps created before February 7, 2024 will work as expected with no change. See our blog post to learn more.
Displays the wallet widget within an iframe that prompts the user to consent to sharing information with the requesting dApp with OpenID profile scopes. Currently, the only profile scope that can be requested is a verified email. Collecting a verified email address from third-party wallet users (MetaMask, Coinbase Wallet, etc.) is a premium feature but included in the free trial period (see pricing). User must be signed in for this method to return or else it will throw an error.
Only available with Universal Wallet.
#Arguments
scope?
(Object): The object containing requested OpenID profile scopesemail?: String
: If the user should be prompted to provide them email as arequired
oroptional
field
#Returns
- A
promise
which returns anObject
: Contains result of the requested scopesemail?: String
: The email of the user if they consented to providing it in the UI
#Example
01import { Magic } from "magic-sdk"
02
03// after user has already logged in
04const userInfo = await magic.user.requestInfoWithUI({ scope: { email: "required" }})
05console.log(userInfo.email); // the user's email if they consented.
#onUserLoggedOut
When the useStorageCache is enabled, there might be situations where the isLoggedIn function returns true despite the user being logged out. In such instances, an event will be emitted after a few milliseconds, providing an opportunity to manage the user's logged-out state, such as when a session expires.
Only necessary with when the useStorageCache
option is set to true
.
#Arguments
callback
((isLoggedOut: boolean) => void
): The callback function when the event is emitted
#Returns
- A
function
that can be called to unsubscribe from the event
#Example
01import { Magic } from "magic-sdk"
02
03// Create Magic instance with useStorageCache set to true
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 useStorageCache: true
06});
07
08magic.user.onUserLoggedOut((isLoggedOut: boolean) => {
09 // Do something when user is logged out
10 navigation.navigate('LoginScreen')
11})
#OAuth Module
The OAuth Module and it's members are accessible on the Magic SDK instance by the oauth2
property.
To use the OAuth Module in your application, install @magic-ext/oauth2
along with magic-sdk
.
01import { Magic } from 'magic-sdk';
02import { OAuthExtension } from '@magic-ext/oauth2';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new OAuthExtension()],
06});
07
08magic.oauth2;
09magic.oauth2.loginWithRedirect;
10magic.oauth2.getRedirectResult;
#loginWithRedirect
Starts the OAuth 2.0 login flow.
Only available with Dedicated Wallet.
#Arguments
provider
(String): The OAuth provider being used for loginredirectURI
(String): A URL a user is sent to after they successfully log inscope?
(Array): Defines the specific permissions an application requests from a user
#Returns
- None
#Valid Providers
Name | Argument | Example |
| ||
| ||
| ||
Apple |
| |
Discord |
| |
GitHub |
| |
| ||
Bitbucket |
| |
GitLab |
| |
Twitch |
| |
Microsoft |
|
#Example
01import { Magic } from 'magic-sdk';
02import { OAuthExtension } from '@magic-ext/oauth2';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new OAuthExtension()],
06});
07
08await magic.oauth2.loginWithRedirect({
09 provider: '...' /* 'google', 'facebook', 'apple', etc. */,
10 redirectURI: 'https://your-app.com/your/oauth/callback',
11 scope: ['user:email'] /* optional */,
12});
#getRedirectResult
Returns the final OAuth 2.0 result.
Only available with Dedicated Wallet.
#Arguments
lifespan?
(Number): Set the lifespan of the resolved Decentralize ID token. Defaults to 900s (15 mins)
#Returns
PromiEvent<object>
:-
magic
(Object): Information about the authenticated Magic user. Comprised ofidToken
anduserMetadata
. oauth
(Object): Information about the authenticated OAuth user. Comprised ofaccessToken
,provider
,scope
,userHandle
, anduserInfo
.
-
#Example
01import { Magic } from 'magic-sdk';
02import { OAuthExtension } from '@magic-ext/oauth2';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new OAuthExtension()],
06});
07
08// optionally pass in number to method to set Decentralized ID token lifespan
09const result = await magic.oauth2.getRedirectResult();
10
11// Result has the following interface
12interface OAuthRedirectResult {
13 magic: {
14 idToken: string;
15 userMetadata: MagicUserMetadata;
16 },
17 oauth: {
18 provider: string;
19 scope: string[];
20 accessToken: string;
21 userHandle: string;
22 userInfo: ...;
23 }
24};
#OpenID Module
This module requires an enterprise agreement. For more details click here.
The OpenID Module and it's members are accessible on the Magic SDK instance by the openid
property.
To use the OpenID Module in your application, install @magic-ext/oidc
along with magic-sdk
.
01import { Magic } from 'magic-sdk';
02import { OpenIdExtension } from '@magic-ext/oidc';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new OpenIdExtension()],
06});
07
08magic.openid;
09magic.openid.loginWithOIDC;
#loginWithOIDC
Authenticate users via your preferred OIDC client.
Only available with Dedicated Wallet.
#Arguments
jwt
(String): The OIDC token from your identity provider.providerId
(String): An alphanumeric ID provided by Magic after successful configuration of your identity provider.lifespan?
(Number): Set the lifespan of the resolved Decentralize ID token. Defaults to 900s (15 mins)
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02import { OpenIdExtension } from '@magic-ext/oidc';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new OpenIdExtension()],
06});
07
08const DID = await magic.openid.loginWithOIDC({
09 // this oidcToken comes from the identity provider
10 jwt: oidcToken,
11 // this providerId is provided by Magic
12 providerId: myProviderId,
13 // this optional argument sets the resolved Decentralized ID token's lifespan
14 lifespan: number
15});
#WebAuthn Module
The WebAuthn Module and it's members are accessible on the Magic SDK instance by the webauthn
property.
To use the WebAuthn Module in your application, install @magic-ext/webauthn
along with magic-sdk
.
01import { Magic } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('PUBLISHABLE_API_KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08magic.webauthn;
09magic.webauthn.registerNewUser;
10magic.webauthn.login;
11magic.webauthn.getMetadata;
12magic.webauthn.registerNewDevice;
13magic.webauthn.updateInfo;
14magic.webauthn.unregisterDevice;
#registerNewUser
Registers a new WebAuthn user.
Only available with Dedicated Wallet.
#Arguments
username
(String): The username to register withnickname?
(String): The nickname of the WebAuthn device the user wants to set
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// register a user by their username
09try {
10 const token = await magic.webauthn.registerNewUser({ username: 'username' });
11} catch (e) {
12 // Handle errors if required!
13}
14
15// log in a user by their username and set webauthn device nickname.
16try {
17 const token = await magic.webauthn.registerNewUser({ username: 'username', nickname: 'nickname' });
18} catch (e) {
19 // Handle errors if required!
20}
#Error
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08try {
09 await magic.webauthn.registerNewUser({ username: 'username' });
10} catch (err) {
11 if (err instanceof SDKErrorCode) {
12 switch (err.code) {
13 case SDKErrorCode.WebAuthnNotSupported:
14 case SDKErrorCode.WebAuthnCreateCredentialError:
15 // Handle errors accordingly :)
16 break;
17 }
18 }
19}
#login
Logs in a registered WebAuthn user.
Only available with Dedicated Wallet.
#Arguments
username
(String): The username to log in with
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// login a user by their username
09try {
10 const token = await magic.webauthn.login({ username: 'username' });
11} catch (e) {
12 // Handle errors if required!
13}
#Error
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08try {
09 await magic.webauthn.login({ username: 'username' });
10} catch (err) {
11 if (err instanceof SDKErrorCode) {
12 switch (err.code) {
13 case SDKErrorCode.WebAuthnNotSupported:
14 case SDKErrorCode.WebAuthnCreateCredentialError:
15 // Handle errors accordingly :)
16 break;
17 }
18 }
19}
#getMetadata
Returns information about the currently logged in user.
Only available with Dedicated Wallet.
#Arguments
- None
#Returns
PromiEvent<object | null>
: an object containing the WebAuthn devices information and username of the authenticated userdevicesInfo
(Array): List of devices info contains device ID, device nickname, transport, user agentusername
(String): Username of the authenticated user
#Example
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// Initiates the flow to get webauthn metadata for current account.
09try {
10 const metadata = await magic.webauthn.getMetadata();
11
12 /* webauthn metadata
13 {
14 "devicesInfo": [
15 {
16 "id": "EjI_EFJhB6cdCj6rHPRHUcFCn6NnywALuWjQyPe0_dI=",
17 "nickname": "",
18 "transport": "internal",
19 "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
20 }
21 ],
22 "username": "username"
23 }
24 */
25} catch (e) {
26 // Handle errors if required!
27}
#registerNewDevice
Registers a new WebAuthn device.
Only available with Dedicated Wallet.
#Arguments
nickname
(String): The nickname of the WebAuthn device the user wants to set
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if register webauthn device is successful and rejects with a specific error code if the request fails.
#Example
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// Initiates the flow to a new WebAuthn Device for current account.
09try {
10 await magic.webauthn.registerNewDevice('new device nickname');
11} catch (e) {
12 // Handle errors if required!
13}
#Error
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08try {
09 await magic.webauthn.registerNewDevice('new device nickname');
10} catch (err) {
11 if (err instanceof SDKErrorCode) {
12 switch (err.code) {
13 case SDKErrorCode.WebAuthnNotSupported:
14 case SDKErrorCode.WebAuthnCreateCredentialError:
15 // Handle errors accordingly :)
16 break;
17 }
18 }
19}
#updateInfo
Allows a user to update their device nickname.
Only available with Dedicated Wallet.
#Arguments
nickname
(String): The nickname of the WebAuthn device the user wants to setid
(String): The WebAuthn ID returned fromgetMetaData
function'sdevicesInfo
field
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if update webauthn device nickname is successful and rejects with a specific error code if the request fails
#Example
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// Initiates the flow to update WebAuthn Device's nickname.
09try {
10 await magic.webauthn.updateInfo({ id, nickname });
11} catch (e) {
12 // Handle errors if required!
13}
#unregisterDevice
Unregisters a user's device.
Only available with Dedicated Wallet.
#Arguments
id
(String): The WebAuthn ID returned fromgetMetaData
function'sdevicesInfo
field
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if unregister webauthn device is successful and rejects with a specific error code if the request fails
#Example
01import { Magic, SDKErrorCode } from 'magic-sdk';
02import { WebAuthnExtension } from '@magic-ext/webauthn';
03
04const magic = new Magic('YOUR API KEY', {
05 extensions: [new WebAuthnExtension()],
06});
07
08// Initiates the flow to unregister WebAuthn Device.
09try {
10 /* Assuming user is logged in */
11 await magic.webauthn.unregisterDevice(id);
12} catch (e) {
13 // Handle errors if required!
14}
#Response and Error Handling
There are three types of error class to be aware of when working with Magic's client-side JavaScript SDK:
SDKError
: Raised by the SDK to indicate missing parameters, communicate deprecation notices, or other internal issues. A notable example would be a MISSING_API_KEY error, which informs the required API key parameter was missing from new Magic(...).RPCError
: Errors associated with specific method calls to the Magic<iframe>
context. These methods are formatted as JSON RPC 2.0 payloads, so they return error codes as integers. This type of error is raised by methods likeAuthModule.loginWithMagicLink
.ExtensionError
: Errors associated with method calls to Magic SDK Extensions. Extensions are an upcoming/experimental feature of Magic SDK. More information will be available once Extensions are officially released.
#SDKError
The SDKError
class is exposed for instanceof
operations.
01import { SDKError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof SDKError) {
07 // Handle...
08 }
09}
SDKError
instances expose the code
field which may be used to deterministically identify the error. Additionally, an enumeration of error codes is exposed for convenience and readability:
01import { SDKErrorCode } from 'magic-sdk';
02
03SDKErrorCode.MissingApiKey;
04SDKErrorCode.ModalNotReady;
05SDKErrorCode.MalformedResponse;
06// and so forth...
07// Please reference the `Enum Key` column of the error table below.
#Error Codes
Enum Key | Description |
MissingApiKey | Indicates the required Magic API key is missing or invalid. |
ModalNotReady | Indicates the Magic iframe context is not ready to receive events. This error should be rare and usually indicates an environmental issue or improper async/await usage. |
MalformedResponse | Indicates the response received from the Magic iframe context is malformed. We all make mistakes (even us), but this should still be a rare exception. If you encounter this, please be aware of phishing! |
InvalidArgument | Raised if an SDK method receives an invalid argument. Generally, TypeScript saves us all from simple bugs, but there are validation edge cases it cannot solve—this error type will keep you informed! |
ExtensionNotInitialized | Indicates an extension method was invoked before the Magic SDK instance was initialized. Make sure to access extension methods only from the Magic SDK instance to avoid this error. |
| Indicates that incompatible extensions were detected during the initialization of the Magic SDK. The error message specifies the incompatible extensions and their compatibility requirements based on the current Magic SDK version and platform environment. |
#RPCError
The RPCError
class is exposed for instanceof
operations:
01import { RPCError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof RPCError) {
07 // Handle...
08 }
09}
RPCError
instances expose the code
field which may be used to deterministically identify the error. Additionally, an enumeration of error codes is exposed for convenience and readability:
01import { RPCErrorCode } from 'magic-sdk';
02
03RPCErrorCode.MagicLinkExpired;
04RPCErrorCode.UserAlreadyLoggedIn;
05RPCErrorCode.ParseError;
06RPCErrorCode.MethodNotFound;
07RPCErrorCode.InternalError;
08// and so forth...
09// Please reference the `Enum Key` column of the error table below.
#Magic Link Error Codes
Code | Enum Key | Description |
-10000 | MagicLinkFailedVerification | The magic link failed verification, possibly due to an internal service error or a generic network error. |
-10001 | MagicLinkExpired | The user clicked their magic link after it had expired (this can happen if the user takes more than 20 minutes to check their email). |
-10002 | MagicLinkRateLimited | If the showUI parameter is set to false , this error will communicate the email rate limit has been reached. Please debounce your method calls if this occurs. |
-10006 | MagicLinkInvalidRedirectURL | If using the redirectURI parameter for the magic link flow, this error is recevied if the provided value is invalid for some reason. |
-10003 | UserAlreadyLoggedIn | A user is already logged in. If a new user should replace the existing user, make sure to call logout before proceeding. |
-10004 | UpdateEmailFailed | An update email request was unsuccessful, either due to an invalid email being supplied or the user canceled the action. |
-10005 | UserRequestEditEmail | The user has stopped the login request because they want to edit the provided email. |
-10010 |
| The recipient account is currently inactive. The user should verify and activate their account to resolve this issue. |
-10011 |
| User access is denied. The user lacks the necessary permissions or credentials to perform the requested action. |
-10015 |
| The redirect login process has been successfully completed. |
#Standard JSON RPC 2.0 Error Codes
Code | Enum Key | Description |
-32700 | ParseError | Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. |
-32600 | InvalidRequest | The JSON sent is not a valid Request object. |
-32601 | MethodNotFound | The method does not exist / is not available. |
-32602 | InvalidParams | Invalid method parameter(s). |
-32603 | InternalError | Internal JSON-RPC error. These can manifest as different generic issues (i.e.: attempting to access a protected endpoint before the user is logged in). |
#ExtensionError
The ExtensionError
class is exposed for instanceof
operations:
01import { ExtensionError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof ExtensionError) {
07 // Handle...
08 }
09}
ExtensionError
instances expose the code
field which may be used to deterministically identify the error. Magic SDK does not export a global enumeration of Extension error codes. Instead, Extension authors are responsible for exposing and documenting error codes relevant to the Extension's use-case.
#PromiEvents
Magic SDK provides a flexible interface for handling methods which encompass multiple "stages" of an action. Promises
returned by Magic SDK resolve when a flow has reached finality, but certain methods also contain life-cycle events that dispatch throughout. We refer to this interface as a PromiEvent
. There is prior art to inspire this approach in Ethereum's Web3 standard.
PromiEvent
is a portmanteau of Promise
and EventEmitter
. Browser and React Native SDK methods return this object type, which is a native JavaScript Promise
overloaded with EventEmitter
methods. This value can be awaited
in modern async/await
code, or you may register event listeners to handle method-specific life-cycle hooks. Each PromiEvent
contains the following default event types:
"done"
: Called when thePromise
resolves. This is equivalent toPromise.then
."error"
: Called if thePromise
rejects. This is equivalent toPromise.catch
."settled"
: Called when thePromise
either resolves or rejects. This is equivalent toPromise.finally
.
Look for additional event types documented near the method they relate to. Events are strongly-typed by TypeScript to offer developer hints and conveniant IDE auto-complete.
01const req = magic.auth.loginWithMagicLink({ email: 'hello@magic.link' });
02
03req
04 .on('email-sent', () => {
05 /* ... */
06 })
07 .then(DIDToken => {
08 /* ... */
09 })
10 .once('email-not-deliverable', () => {
11 /* ... */
12 })
13 .catch(error => {
14 /* ... */
15 })
16 .on('error', error => {
17 /* ... */
18 });
#EVM RPC Methods
Magic supports the following EVM RPC Methods that can be called through a web3 provider library such as web3.js
or ethers.js
.
Note: starting from magic-sdk@17.0.0
, eth_accounts
will return an empty array if no user is logged in, instead of prompting the login form. To prompt the login form, use connectWithUI()
.
eth_accounts
get_balance
eth_estimateGas
eth_gasPrice
eth_sendTransaction
personal_sign
eth_signTypedData_v3
eth_signTypedData_v4
#Examples
#Re-authenticate Users
A user's Magic SDK session persists up to 7 days by default, so re-authentication is usually friction-less.
Note: the session length is customizable by the developer through the Magic dashboard.
Before re-authenticating a user, install the Magic Client SDK.
01import { Magic } from 'magic-sdk';
02const magic = new Magic('PUBLISHABLE_API_KEY');
03
04const email = 'example@magic.link';
05
06if (await magic.user.isLoggedIn()) {
07 const didToken = await magic.user.getIdToken();
08
09 // Do something with the DID token.
10 // For instance, this could be a `fetch` call
11 // to a protected backend endpoint.
12 document.getElementById('your-access-token').innerHTML = didToken;
13} else {
14 // Log in the user
15 const user = await magic.auth.loginWithMagicLink({ email });
16}
#Resources
#Versions
All changes to the SDK are covered in our latest release notes.