Integration
Node.js
AgeOnce integration with Node.js (Express, Fastify, Next.js)
Node.js Integration
Complete AgeOnce integration example with Node.js.
Install dependencies
npm install jsonwebtoken jwks-rsaConfiguration
// config.js
module.exports = {
clientId: process.env.AGEONCE_CLIENT_ID,
clientSecret: process.env.AGEONCE_CLIENT_SECRET,
redirectUri: process.env.AGEONCE_REDIRECT_URI,
appUrl: process.env.AGEONCE_APP_URL || 'https://app.ageonce.com',
apiUrl: process.env.AGEONCE_API_URL || 'https://api.ageonce.com',
};Generate verification URL
const crypto = require('crypto');
const config = require('./config');
function generateVerifyUrl(ageRequired = 18) {
const state = crypto.randomBytes(16).toString('hex');
const url = new URL(config.appUrl);
url.searchParams.set('client_id', config.clientId);
url.searchParams.set('redirect_uri', config.redirectUri);
url.searchParams.set('state', state);
url.searchParams.set('age_required', String(ageRequired));
return {
url: url.toString(),
state: state, // Save in session for verification
};
}Exchange code for token
async function exchangeCodeForToken(code, stateFromCallback) {
const credentials = Buffer.from(
`${config.clientId}:${config.clientSecret}`
).toString('base64');
const response = await fetch(`${config.apiUrl}/api/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${credentials}`,
},
body: JSON.stringify({
grant_type: 'authorization_code',
code,
redirect_uri: config.redirectUri,
...(stateFromCallback && { state: stateFromCallback }),
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error_description || 'Token exchange failed');
}
return response.json();
}Token validation
async function validateToken(token) {
const response = await fetch(
`${config.apiUrl}/api/verify/token/${encodeURIComponent(token)}`
);
const data = await response.json();
if (!response.ok || !data.valid) {
return { valid: false };
}
return {
valid: true,
age_verified: data.age_verified,
age_over: data.age_over,
};
}const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `${config.apiUrl}/api/.well-known/jwks.json`,
cache: true,
cacheMaxAge: 86400000, // 24 hours
});
function getKey(header, callback) {
client.getSigningKeys((err, keys) => {
if (err) return callback(err);
callback(null, keys[0].getPublicKey());
});
}
async function validateTokenLocally(token) {
return new Promise((resolve) => {
jwt.verify(token, getKey, {
algorithms: ['RS256'],
issuer: 'https://ageonce.io',
}, (err, decoded) => {
if (err) {
resolve({ valid: false, error: err.message });
} else {
resolve({ valid: true, payload: decoded });
}
});
});
}Express example
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
}));
// Initiate verification
app.get('/verify', (req, res) => {
const { url, state } = generateVerifyUrl();
req.session.oauthState = state;
res.redirect(url);
});
// Callback handler
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state
if (state !== req.session.oauthState) {
return res.status(400).send('Invalid state');
}
try {
const { access_token } = await exchangeCodeForToken(code, state);
const result = await validateToken(access_token);
if (result.valid && result.age_verified) {
req.session.ageVerified = true;
req.session.ageOver = result.age_over;
res.redirect('/protected-content');
} else {
res.status(403).send('Age verification failed');
}
} catch (error) {
res.status(500).send('Verification error');
}
});
// Middleware for protected content
function requireAgeVerification(req, res, next) {
if (req.session.ageVerified) {
next();
} else {
res.redirect('/verify');
}
}
app.get('/protected-content', requireAgeVerification, (req, res) => {
res.send('Welcome to age-restricted content!');
});
app.listen(3000);This example can be adapted for Fastify, Koa, Next.js API routes, and other Node.js frameworks.