openid-client
This guide walks you through the basic steps to integrate with the AuthN Authorization Server using the openid-client library.
Create an OAuth 2 client
Create a new OAuth client and configure it for the Authorization Code grant, as described in the OAuth Server documentation. Use the following parameters:
- Grant Types -
Authorization Code
- Response Types -
Code
,Token
- Allowed Redirect URIs -
http://localhost:3000/api/oauth/redirect/pangea
- Allowed Scopes -
profile
Set up environment
If your client software supports OpenID Connect Discovery, you can provide the OAuth 2.0 Authorization Server Metadata endpoint, available on the AuthN OAuth Server page in your Pangea User Console .
PANGEA_AUTHN_AUTHORIZATION_SERVER_METADATA_URL="https://pdn-k2tn7tp7iy5rkap2vtomjjfiod2o22bc.login.dev.aws.pangea.cloud/.well-known/oauth-authorization-server"
PANGEA_AUTHN_CLIENT_ID="psa_b7txo756qv3van54vjjou52tznoubkui"
PANGEA_AUTHN_CLIENT_SECRET="pck_f7wcspye3jxj3s3vxp4auchgvyjznjmx"
PANGEA_AUTHN_REDIRECT_URI="http://localhost:3000/api/oauth/redirect/pangea"
Implement Authorization Code grant
import * as client from "openid-client";
import dotenv from "dotenv";
import * as readline from "readline/promises";
dotenv.config();
// Configure the client.
const issuerUrl = new URL(
process.env.PANGEA_AUTHN_AUTHORIZATION_SERVER_METADATA_URL ||
"<your-issuer-identifier-url>",
);
const clientId = process.env.PANGEA_AUTHN_CLIENT_ID || "<your-client-id>";
const clientSecret =
process.env.PANGEA_AUTHN_CLIENT_SECRET || "<your-client-secret>";
const redirectUri =
process.env.PANGEA_AUTHN_REDIRECT_URI || "<your-redirect-uri>";
// Discover authorization server configuration.
const config = await client.discovery(issuerUrl, clientId, {
client_secret: clientSecret,
});
// Prepare PKCE and generate state.
const codeVerifier = client.randomPKCECodeVerifier();
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
const state = client.randomState();
// Build authorization URL.
const authorizationUrl = client.buildAuthorizationUrl(config, {
redirect_uri: redirectUri,
scope: "profile",
code_challenge: codeChallenge,
code_challenge_method: "S256",
state,
});
// Display the authorization URL.
console.log("\nRedirect user to:", authorizationUrl.href);
// Wait for user to paste the redirect URL after authorization.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const redirectUrl = await rl.question("\nPaste the full redirect URL here: ");
rl.close();
// Simulate getting current redirect URL from browser.
function getCurrentUrl() {
return new URL(redirectUrl);
}
const currentUrl = getCurrentUrl();
// Exchange authorization code for access token.
const { access_token } = await client.authorizationCodeGrant(
config,
currentUrl,
{
pkceCodeVerifier: codeVerifier,
expectedState: state,
},
);
console.log("\nAccess Token:", access_token);
// Call a protected resource
const resourceUrl = new URL(
config.serverMetadata().userinfo_endpoint || "<your-protected-resource-url>",
);
const response = await client.fetchProtectedResource(
config,
access_token,
resourceUrl,
"GET",
);
console.log("\n Protected Resource Response:", await response.json());
-
The authorization URL will be printed to standard output.
Redirect to the authorization endpointRedirect user to: https://pdn-k2tn7tp7iy5rkap2vtomjjfiod2o22bc.login.dev.aws.pangea.cloud/v2/oauth/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fredirect&scope=profile&code_challenge=R1kiFGu9rhKaAR6hOZbuVjgODNj_yh9lzsenufsC5So&code_challenge_method=S256&state=h9_MRwB26H7Ip8_RSftBFn4rXe6O3qUrej3bPhG66ec&client_id=psa_b7txo756qv3van54vjjou52tznoubkui&response_type=code
Paste the full redirect URL here:In your browser, open the displayed URL. After signing in and approving the request, the authorization server will redirect you to the specified redirect URI.
Copy the redirect URL from your browser’s address bar and paste it into the prompt when requested. For example:
Paste the full redirect URL here: http://localhost:3000/redirect?code=poc_2roaf2qkonm2bxzrz7tgcfrb2hxyfdtu&state=h9_MRwB26H7Ip8_RSftBFn4rXe6O3qUrej3bPhG66ec
-
The access token will be printed to standard output.
Access Token: ptu_ngkptg5kq74glky7ydwvkmtznqe2i7ti
-
The protected resource response (JSON) will be printed to standard output.
Protected resource responseProtected Resource Response: {
sub: 'pui_57opch7rs2meq4lf2gmtapl44moyl37k',
given_name: 'Alice',
preferred_username: 'alice',
email: 'alice@wonderland.com',
email_verified: true,
updated_at: '0001-01-01T00:00:00Z',
intelligence: {
embargo: false,
ip_intel: {
is_bad: false,
reputation: [Object],
geolocation: [Object],
is_vpn: false,
is_proxy: true
},
domain_intel: { is_bad: false, reputation: [Object] },
user_intel: false
},
last_login_ip: '81.141.220.74',
last_login_city: 'Oxford',
last_login_country: 'England',
authenticators: [
{
id: 'pau_qbv6nsj52vj2f34kpisn3pltumtm7ucz',
type: 'email_otp',
enabled: true,
phase: 'secondary',
enrolling_browser: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0',
enrolling_ip: '81.141.220.74',
created_at: '2025-04-10T02:06:16.920575Z',
updated_at: '2025-04-30T05:48:57.265655Z',
state: 'active'
},
{
id: 'pau_u5mgccytzv6bynvimirjd2p4dw6c3iwa',
type: 'password',
enabled: true,
phase: 'primary',
enrolling_browser: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:137.0) Gecko/20100101 Firefox/137.0',
enrolling_ip: '81.141.220.74',
created_at: '2025-04-10T02:05:21.436905Z',
updated_at: '2025-04-30T18:59:35.076293Z',
state: 'active'
}
]
}
Was this article helpful?