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
| Setting | Value |
|---|---|
| Issuer URL | https://sso.alien-api.com |
| Client ID | Your provider address |
| Client Secret | Empty string (public client) |
| Token Auth Method | none |
| PKCE | Required (S256) |
| Response Type | code |
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 } = handlersWith 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 use3. 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.comNext Steps
- Core Integration Guide - Use our JavaScript SDK
- React Integration Guide - React SDK with hooks
- API Reference - Complete SDK documentation
Last updated on