@topolo/login
v0.2.0
Published
Public Login with Topolo helpers for OAuth/OIDC provider integrations.
Downloads
211
Readme
@topolo/login
Public helpers for adding Login with Topolo to an app that already has its own auth.
Use this package for OAuth/OIDC provider setup, token exchange, userinfo, ID-token verification, and account-link payloads. Use @topolo/sdk after login when your app needs to call Topolo APIs with the issued access_token.
Install
npm install @topolo/loginAdd Login with Topolo
import {
createTopoloAuthorizationUrl,
exchangeTopoloCode,
fetchTopoloUserInfo,
refreshTopoloToken,
verifyTopoloIdToken,
createAccountLinkPayload,
} from '@topolo/login';
const auth = await createTopoloAuthorizationUrl({
clientId: process.env.TOPOLO_CLIENT_ID!,
redirectUri: 'https://app.example.com/auth/topolo/callback',
scopes: ['openid', 'profile', 'email', 'organization:read', 'entitlements:read'],
});
// Store auth.state and auth.codeVerifier in the user's session, then redirect:
console.log(auth.url);
const tokenSet = await exchangeTopoloCode({
clientId: process.env.TOPOLO_CLIENT_ID!,
clientSecret: process.env.TOPOLO_CLIENT_SECRET,
redirectUri: 'https://app.example.com/auth/topolo/callback',
code: callbackQuery.code,
codeVerifier: session.topoloCodeVerifier,
});
const profile = await fetchTopoloUserInfo({ accessToken: tokenSet.access_token });
if (tokenSet.id_token) {
await verifyTopoloIdToken({
idToken: tokenSet.id_token,
issuer: process.env.TOPOLO_AUTH_BASE_URL ?? 'https://auth.topolo.app',
audience: process.env.TOPOLO_CLIENT_ID!,
});
}
const link = createAccountLinkPayload({
localUserId: currentUser.id,
userInfo: profile,
});
const refreshed = await refreshTopoloToken({
clientId: process.env.TOPOLO_CLIENT_ID!,
clientSecret: process.env.TOPOLO_CLIENT_SECRET,
refreshToken: tokenSet.refresh_token!,
});Never auto-merge accounts solely by matching email. Link to an existing local user only when that user is already signed in, or after explicit confirmation. Store Topolo refresh tokens using your app's existing server-side encrypted token pattern; do not expose refresh tokens to browser sessions.
Auth.js / NextAuth
import NextAuth from 'next-auth';
import { createTopoloAuthJsProvider, refreshTopoloToken } from '@topolo/login';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
createTopoloAuthJsProvider({
clientId: process.env.TOPOLO_CLIENT_ID!,
clientSecret: process.env.TOPOLO_CLIENT_SECRET,
scopes: ['openid', 'profile', 'email'],
}),
],
});Use the Auth.js jwt callback to keep refresh_token server-side and call refreshTopoloToken before the Topolo access token expires. A full example lives in examples/login-with-topolo/authjs.
Express / Passport
import { Strategy as OAuth2Strategy } from 'passport-oauth2';
import { createTopoloPassportStrategyConfig, fetchTopoloUserInfo, refreshTopoloToken } from '@topolo/login';
passport.use('topolo', new OAuth2Strategy(
createTopoloPassportStrategyConfig({
clientId: process.env.TOPOLO_CLIENT_ID!,
clientSecret: process.env.TOPOLO_CLIENT_SECRET,
callbackUrl: 'https://app.example.com/auth/topolo/callback',
}),
async (accessToken, refreshToken, _params, _profile, done) => {
const userInfo = await fetchTopoloUserInfo({ accessToken });
done(null, { userInfo, refreshToken });
},
));Use refreshTopoloToken from server-side middleware or route handlers before calling Topolo APIs with an expired access token. A full example lives in examples/login-with-topolo/express.
Environments
Production Auth issuer: https://auth.topolo.app
Staging Auth issuer: https://auth.stg.topolo.us
