Login widget
Add sdk.js and call login — the sign-in window opens, and onSuccess returns the authorization
code. Below: the call on each stack, the parameters, window configuration and error codes.
An end-to-end “from zero to token” example is the quick start.
The login method
Section titled “The login method”Every tab is self-contained — connect and call.
<script src="https://id.fidmos.com/js/sdk.js" data-client-id="YOUR_CLIENT_ID"></script><script>
window.FidmosID.login({
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://partner.example/callback',
scope: 'openid profile email',
onSuccess: async ({ code, state, codeVerifier }) => {
// exchange code for a token on your backend: POST /login/callback
await fetch('/login/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state, codeVerifier }),
});
location.assign('/logged-in');
},
onCancel: () => {
// the user closed the window — this is not an error
},
onError: ({ code }) => {
// code: 'invalid_arguments' | 'already_open' | 'unavailable'
},
});
</script>npm install fidmos-idimport { useState } from 'react';
import { login } from 'fidmos-id';
export function LoginButton() {
const [busy, setBusy] = useState(false);
const signIn = () => {
setBusy(true);
login({
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://partner.example/callback',
scope: 'openid profile email',
onSuccess: async ({ code, state, codeVerifier }) => {
// exchange code for a token on your backend: POST /login/callback
await fetch('/login/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state, codeVerifier }),
});
location.assign('/logged-in');
},
onCancel: () => setBusy(false),
onError: () => setBusy(false),
});
};
return (
<button type="button" onClick={signIn} disabled={busy}>
Sign in with Fidmos
</button>
);
}npm install fidmos-id<script setup>
import { ref } from 'vue';
import { login } from 'fidmos-id';
const busy = ref(false);
function signIn() {
busy.value = true;
login({
clientId: 'YOUR_CLIENT_ID',
redirectUri: 'https://partner.example/callback',
scope: 'openid profile email',
onSuccess: async ({ code, state, codeVerifier }) => {
// exchange code for a token on your backend: POST /login/callback
await fetch('/login/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state, codeVerifier }),
});
location.assign('/logged-in');
},
onCancel: () => (busy.value = false),
onError: () => (busy.value = false),
});
}
</script>
<template>
<button type="button" :disabled="busy" @click="signIn">Sign in with Fidmos</button>
</template>| Parameter | Purpose |
|---|---|
clientId |
your client identifier |
redirectUri |
your application’s return address — the same one registered in the dashboard |
scope |
requested data, e.g. 'openid profile email'; openid is required for id_token |
state |
optional; your string — returned in onSuccess unchanged |
nonce |
optional; goes into id_token — then verify it on the backend (expectedNonce), otherwise the built-in check rejects the token |
config |
window appearance: methods, wallets, language, texts — Window configuration |
onSuccess({ code, state, codeVerifier }) |
sign-in succeeded — the authorization code and the PKCE code_verifier |
onCancel() |
the user closed the window |
onError({ code }) |
the window did not open |
code is one-time and does not yield a token on its own: exchange it on your backend at
/oauth/token, passing the codeVerifier from onSuccess and your client_secret — as in the
quick start.
Window configuration
Section titled “Window configuration”The optional config object changes the appearance of the window — login methods, the wallet
list, language and texts. It is client-side: it lives in your login({ config }) call and
changes nothing on the server (the boundary — origin, redirect_uri, scopes — is held by the
server). Assemble it visually and copy ready-made code in the configurator.
| Option | Type | Default | What it sets |
|---|---|---|---|
methods |
('email' | 'telegram' | 'wallet')[] |
all three | which login methods to show |
wallets |
string[] (rdns) |
the whole catalog | the set of wallets (see below) |
locale |
'ru' | 'en' |
'ru' |
the window’s interface language |
theme |
'light' | 'dark' | 'auto' |
'dark' |
the window theme; auto follows the system prefers-color-scheme |
title |
string |
“Sign in” | the window title |
terms.tos |
string (URL) |
— | a link to your Terms of Service |
terms.privacy |
string (URL) |
— | a link to your Privacy Policy |
The config object is the same on any stack — it is the second field in login({ …, config })
(the call shape on each stack is in the login method):
config: {
methods: ['email', 'wallet'],
wallets: ['io.metamask', 'io.rabby', 'app.phantom'],
locale: 'en',
theme: 'light',
title: 'Sign in to Acme',
terms: {
tos: 'https://partner.example/tos',
privacy: 'https://partner.example/privacy',
},
}
Terms links are taken only if they are a full http(s) URL; otherwise the string is dropped.
wallets — the set, not the order
Section titled “wallets — the set, not the order”wallets is an allow-list by rdns: it sets which wallets to show. The order in the window is
always the catalog order (by popularity), and the wallets the user has installed rise to the top —
wallets does not set the order. An empty or missing wallets means the whole catalog.
| Wallet | rdns |
|---|---|
| MetaMask | io.metamask |
| Rabby | io.rabby |
| Trust Wallet | com.trustwallet.app |
| Coinbase Wallet | com.coinbase.wallet |
| Phantom | app.phantom |
| OKX Wallet | com.okex.wallet |
| Rainbow | me.rainbow |
| Zerion | io.zerion.wallet |
| Brave Wallet | com.brave.wallet |
An install prompt for an undetected wallet is provided only by these rdns; a wallet with any other
rdns shows only if it is detected in the browser.
onError outcomes
Section titled “onError outcomes”| Code | What happened |
|---|---|
invalid_arguments |
clientId or redirectUri was not passed |
already_open |
the window is already open |
unavailable |
the window did not load (network, timeout, closed before it was ready) |
Cancellation (onCancel) is not an error — do not show an error message for it.
A breakdown of all error codes is in the reference.