Guide: The 'Login with NHBCoin' SDK
Provide your users with a secure, seamless, and passwordless login experience. Our 'Login with NHBCoin' kit uses a standard OAuth 2.0 authorization-code flow, returning both a verified email address and wallet identifiers for NHB and ZapNHB so you can safely associate accounts without touching private keys.
Step 1: Register Your Application
Before you can use the Login Kit, register your application from your business workspace's Developer dashboard (OAuth Apps tab). You will need to provide a name for your app and specify a 'Redirect URI' -- the secure URL on your site where users are sent back to after they approve the login. Upon registration, you will receive a `client_id` and, shown once, a `client_secret`.
Step 2: The Frontend Login Flow
Add a 'Login with NHBCoin' button to your site. When clicked, it should redirect the user to our authorization endpoint, including your `client_id`, your registered `redirect_uri`, and the consent scopes (`read:profile wallet:read`) that authorize access to the email and wallet metadata.
// 1. User clicks a button that redirects them
const loginUrl = new URL('https://nhbcoin.com/authorize');
loginUrl.searchParams.set('response_type', 'code');
loginUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
loginUrl.searchParams.set('redirect_uri', 'https://peeksly.com/callback');
loginUrl.searchParams.set('scope', 'read:profile wallet:read');
loginUrl.searchParams.set('state', 'random_string_for_security');
window.location.href = loginUrl.toString();Step 3: The Backend Token Exchange
After the user approves the login, they are redirected back to your 'Redirect URI' with a single-use authorization `code` valid for about a minute. Your backend server must exchange this `code` and your `client_secret` for a short-lived `access_token` (and a `refresh_token` -- see Step 5), then immediately call `/userinfo` to hydrate your records with the verified email and wallet identifiers.
// 2. On your backend, handle the /callback route
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verify the 'state' parameter matches to prevent CSRF attacks
const response = await fetch('https://nhbcoin.com/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code: code,
redirect_uri: 'https://peeksly.com/callback',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET'
})
});
const { access_token } = await response.json();
// 3. Use the access token to get user info
const userResponse = await fetch('https://nhbcoin.com/api/oauth/userinfo', {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const userInfo = await userResponse.json();
// Persist the verified identity for downstream payments
await db.users.upsert({
authId: userInfo.sub,
email: userInfo.email,
emailVerified: userInfo.email_verified,
nhbWallet: userInfo.wallets.nhb,
znhbWallet: userInfo.wallets.znhb
});
// Now you can create a session for the user on your site.
});Step 4: Making Authenticated Requests
Store the enriched profile so downstream payment flows can rely on the verified contact and wallet data without asking the customer again.
- Capture the user's email and `email_verified` flag for receipts and regulatory notices.
- Persist the NHB wallet identifier used for direct layer-one payments.
- Persist the ZapNHB wallet identifier for rollup or off-chain settlement paths.
The `/userinfo` response already contains the normalized structure you need to upsert into your identity store.
{
"sub": "cus_a1b2c3",
"email": "[email protected]",
"email_verified": true,
"wallets": {
"nhb": "nhb1qy352eufqy352eufqy352eufqy352eufqxd7",
"znhb": "znhb1qy352eufqy352eufqy352eufqy352eufqxyz"
}
}Step 5: Refreshing the Access Token
The `access_token` expires after one hour. Before it does, exchange the `refresh_token` you received alongside it for a new pair -- each refresh rotates both tokens, so always store the new `refresh_token` and discard the old one. A refresh token that is presented twice is treated as a sign of compromise: it revokes every outstanding token for your app for that user, requiring them to log in again.
// The access token expires after 1 hour. Before it does, exchange
// the refresh_token you were also given for a fresh pair -- this
// rotates BOTH tokens, so always store the new refresh_token and
// discard the old one.
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://nhbcoin.com/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET'
})
});
if (!response.ok) {
// A 400 here usually means the refresh token was already used once
// before (rotation) -- if that happens unexpectedly, treat it as a
// compromised token and re-authorize the user from scratch.
throw new Error('refresh failed, re-authorize the user');
}
const { access_token, refresh_token } = await response.json();
return { access_token, refresh_token };
}