Skip to Content
⚠️ Note: Some details in this documentation may not be fully accurate yet.
GuideOAuth2 Clients

OAuth2 Clients Integration

Alien SSO is a fully OIDC-compliant identity provider. This means you can use any standard OAuth 2.0 / OpenID Connect library to integrate with it.

Requirements

Before integrating, ensure you have:

  • A registered provider from the developer portal with provider address
  • SSO base URL: https://sso.alien-api.com

Key Configuration Notes

SettingValue
Issuer URLhttps://sso.alien-api.com
Client IDYour provider address
Client SecretEmpty string (public client)
Token Auth Methodnone
PKCERequired (S256)
Response Typecode

NextAuth.js (Auth.js)

NextAuth.js (now Auth.js) is the most popular authentication library for Next.js applications.

Basic Setup

// src/auth.ts import NextAuth from "next-auth" export const { handlers, auth, signIn, signOut } = NextAuth({ providers: [{ id: "alien", name: "Alien", type: "oidc", issuer: "https://sso.alien-api.com", clientId: process.env.ALIEN_PROVIDER_ADDRESS!, clientSecret: "", // Public client - no secret needed client: { token_endpoint_auth_method: "none", }, checks: ["pkce", "state"], authorization: { params: { scope: "openid", }, }, }], })
// src/app/api/auth/[...nextauth]/route.ts import { handlers } from "@/auth" export const { GET, POST } = handlers

With Refresh Tokens

For long-lived sessions with automatic token refresh:

// src/auth.ts import NextAuth from "next-auth" import type { JWT } from "next-auth/jwt" // Token refresh function async function refreshAccessToken(token: JWT): Promise<JWT> { try { const response = await fetch("https://sso.alien-api.com/oauth/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: token.refreshToken as string, client_id: process.env.ALIEN_PROVIDER_ADDRESS!, }), }) const refreshedTokens = await response.json() if (!response.ok) { throw refreshedTokens } return { ...token, accessToken: refreshedTokens.access_token, accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000, refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, idToken: refreshedTokens.id_token ?? token.idToken, } } catch (error) { console.error("Error refreshing access token:", error) return { ...token, error: "RefreshAccessTokenError", } } } export const { handlers, auth, signIn, signOut } = NextAuth({ providers: [{ id: "alien", name: "Alien", type: "oidc", issuer: "https://sso.alien-api.com", clientId: process.env.ALIEN_PROVIDER_ADDRESS!, clientSecret: "", client: { token_endpoint_auth_method: "none", }, checks: ["pkce", "state"], authorization: { params: { scope: "openid", }, }, }], callbacks: { async jwt({ token, account }) { // Initial sign in if (account) { return { ...token, accessToken: account.access_token, accessTokenExpires: account.expires_at! * 1000, refreshToken: account.refresh_token, idToken: account.id_token, } } // Return previous token if the access token has not expired yet if (Date.now() < (token.accessTokenExpires as number)) { return token } // Access token has expired, try to refresh it return await refreshAccessToken(token) }, async session({ session, token }) { session.accessToken = token.accessToken as string session.error = token.error as string | undefined return session }, }, })

Extend Session Types

// src/types/next-auth.d.ts import "next-auth" declare module "next-auth" { interface Session { accessToken?: string error?: string } } declare module "next-auth/jwt" { interface JWT { accessToken?: string accessTokenExpires?: number refreshToken?: string idToken?: string error?: string } }

Handle Token Refresh Errors

// src/components/SessionProvider.tsx "use client" import { useSession, signIn } from "next-auth/react" import { useEffect } from "react" export function SessionRefreshHandler({ children }: { children: React.ReactNode }) { const { data: session } = useSession() useEffect(() => { if (session?.error === "RefreshAccessTokenError") { // Force sign in to resolve the error signIn("alien") } }, [session?.error]) return <>{children}</> }

Usage in Components

// src/app/page.tsx import { auth, signIn, signOut } from "@/auth" export default async function Home() { const session = await auth() if (!session) { return ( <form action={async () => { "use server" await signIn("alien") }}> <button type="submit">Sign in with Alien</button> </form> ) } return ( <div> <p>Welcome, {session.user?.name}</p> <p>User ID: {session.user?.id}</p> <form action={async () => { "use server" await signOut() }}> <button type="submit">Sign out</button> </form> </div> ) }

API Route with Token

// src/app/api/protected/route.ts import { auth } from "@/auth" import { NextResponse } from "next/server" export async function GET() { const session = await auth() if (!session?.accessToken) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) } // Use accessToken for downstream API calls const response = await fetch("https://your-api.com/data", { headers: { Authorization: `Bearer ${session.accessToken}`, }, }) return NextResponse.json(await response.json()) }

Generic OAuth 2.0 Client

For any language or framework, here’s how to implement the OAuth 2.0 flow manually:

1. Discovery Document

First, fetch the OIDC configuration:

const discovery = await fetch( "https://sso.alien-api.com/.well-known/openid-configuration" ) const config = await discovery.json() // config contains: // { // authorization_endpoint: "https://sso.alien-api.com/oauth/authorize", // token_endpoint: "https://sso.alien-api.com/oauth/token", // userinfo_endpoint: "https://sso.alien-api.com/oauth/userinfo", // jwks_uri: "https://sso.alien-api.com/oauth/jwks", // ... // }

2. Generate PKCE Challenge

import crypto from "crypto" function generateCodeVerifier(): string { return crypto.randomBytes(32).toString("base64url") } function generateCodeChallenge(verifier: string): string { return crypto.createHash("sha256").update(verifier).digest("base64url") } const codeVerifier = generateCodeVerifier() const codeChallenge = generateCodeChallenge(codeVerifier) // Store codeVerifier in session for later use

3. Authorization Request

const authUrl = new URL("https://sso.alien-api.com/oauth/authorize") authUrl.searchParams.set("response_type", "code") authUrl.searchParams.set("client_id", "your-provider-address") authUrl.searchParams.set("redirect_uri", "https://your-app.com/callback") authUrl.searchParams.set("scope", "openid") authUrl.searchParams.set("state", generateRandomState()) authUrl.searchParams.set("code_challenge", codeChallenge) authUrl.searchParams.set("code_challenge_method", "S256") // Redirect user to authUrl.toString()

4. Token Exchange

async function exchangeCode(code: string, codeVerifier: string) { const response = await fetch("https://sso.alien-api.com/oauth/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "authorization_code", code, client_id: "your-provider-address", redirect_uri: "https://your-app.com/callback", code_verifier: codeVerifier, }), }) return response.json() // Returns: { access_token, id_token, refresh_token, expires_in, token_type } }

5. Refresh Token

async function refreshToken(refreshToken: string) { const response = await fetch("https://sso.alien-api.com/oauth/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: "your-provider-address", }), }) return response.json() }

6. Verify Token (UserInfo)

async function getUserInfo(accessToken: string) { const response = await fetch("https://sso.alien-api.com/oauth/userinfo", { headers: { Authorization: `Bearer ${accessToken}`, }, }) return response.json() // Returns: { sub: "user-session-address" } }

7. Verify JWT Locally

import jwt from "jsonwebtoken" import jwksClient from "jwks-rsa" const client = jwksClient({ jwksUri: "https://sso.alien-api.com/oauth/jwks", cache: true, rateLimit: true, }) async function verifyToken(token: string): Promise<any> { const decoded = jwt.decode(token, { complete: true }) if (!decoded) throw new Error("Invalid token") const key = await client.getSigningKey(decoded.header.kid) const publicKey = key.getPublicKey() return jwt.verify(token, publicKey, { algorithms: ["RS256"], issuer: "https://sso.alien-api.com", audience: "your-provider-address", }) }

Python (Authlib)

from authlib.integrations.flask_client import OAuth oauth = OAuth() oauth.register( name='alien', client_id='your-provider-address', client_secret='', server_metadata_url='https://sso.alien-api.com/.well-known/openid-configuration', client_kwargs={ 'scope': 'openid', 'code_challenge_method': 'S256', 'token_endpoint_auth_method': 'none', }, ) # Login route @app.route('/login') def login(): redirect_uri = url_for('callback', _external=True) return oauth.alien.authorize_redirect(redirect_uri) # Callback route @app.route('/callback') def callback(): token = oauth.alien.authorize_access_token() user_info = oauth.alien.userinfo() # Store token and user_info in session return redirect('/dashboard')

Go (golang.org/x/oauth2)

package main import ( "context" "golang.org/x/oauth2" ) var oauth2Config = &oauth2.Config{ ClientID: "your-provider-address", ClientSecret: "", // Public client Endpoint: oauth2.Endpoint{ AuthURL: "https://sso.alien-api.com/oauth/authorize", TokenURL: "https://sso.alien-api.com/oauth/token", }, RedirectURL: "http://localhost:8080/callback", Scopes: []string{"openid"}, } // Generate auth URL with PKCE func getAuthURL(state string, codeVerifier string) string { return oauth2Config.AuthCodeURL( state, oauth2.S256ChallengeOption(codeVerifier), ) } // Exchange code for token func exchangeToken(ctx context.Context, code, codeVerifier string) (*oauth2.Token, error) { return oauth2Config.Exchange( ctx, code, oauth2.VerifierOption(codeVerifier), ) } // Refresh token func refreshToken(ctx context.Context, token *oauth2.Token) (*oauth2.Token, error) { tokenSource := oauth2Config.TokenSource(ctx, token) return tokenSource.Token() }

Environment Variables

For any integration, set these environment variables:

# .env ALIEN_PROVIDER_ADDRESS=your-provider-address ALIEN_SSO_URL=https://sso.alien-api.com

Next Steps

Last updated on