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,
apiUrl: process.env.AGEONCE_API_URL || 'https://app.ageonce.com',
};Generate verification URL
const crypto = require('crypto');
const config = require('./config');
function generateVerifyUrl() {
const state = crypto.randomBytes(16).toString('hex');
const params = new URLSearchParams({
client_id: config.clientId,
redirect_uri: config.redirectUri,
state: state,
});
return {
url: `${config.apiUrl}/verify?${params.toString()}`,
state: state, // Save in session for verification
};
}Exchange code for token
async function exchangeCodeForToken(code) {
const response = await fetch(`${config.apiUrl}/api/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: config.clientId,
client_secret: config.clientSecret,
code: code,
redirect_uri: config.redirectUri,
}),
});
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/oauth/validate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token }),
});
if (!response.ok) {
return { valid: false };
}
const data = await response.json();
return {
valid: data.valid,
payload: data.payload,
};
}const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `${config.apiUrl}/api/oauth/jwks`,
cache: true,
cacheMaxAge: 86400000, // 24 hours
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
async function validateTokenLocally(token) {
return new Promise((resolve) => {
jwt.verify(token, getKey, {
algorithms: ['RS256'],
issuer: 'ageonce',
}, (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 { age_token } = await exchangeCodeForToken(code);
const { valid, payload } = await validateToken(age_token);
if (valid && payload.age_verified) {
req.session.ageVerified = true;
req.session.minAge = payload.min_age;
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.