OAuth 2.1
The SDK supports Authorization Code + PKCE, Refresh Token, and Client Credentials (service scopes for confidential clients only).
Important PoE specifics:
- Authorization Code requires PKCE for both public and confidential clients.
- Token endpoint auth method is
client_secret_post(sendclient_secretin the form body). HTTP Basic andclient_secret_jwtare not supported.
Browser Auth Helper (SPA)
See also: Browser Auth (SPA) for storage, lifecycle, and error handling details.
import { createBrowserAuth } from 'poe-js-sdk/browser-auth';
import { PoEApiClient } from 'poe-js-sdk';
const auth = createBrowserAuth({
clientId: '<public-client-id>',
redirectUri: 'http://127.0.0.1:8080/callback',
scopes: ['account:profile'],
});
// Start login
auth.login();
// On your redirectUri page
await auth.handleRedirectCallback();
// Use the access token
const client = new PoEApiClient({
userAgent: 'OAuth myapp/1.0.0 (contact: dev@example.com)',
accessToken: await auth.getAccessToken(),
});
Notes:
- Uses PKCE and
stateautomatically; no secrets in the browser. - Tokens stored in
sessionStorageby default; you can inject a custom storage. - Automatically refreshes when near expiry.
Grants
import { OAuthHelper } from 'poe-js-sdk';
// 1) Build auth URL (PKCE)
const state = crypto.randomUUID();
const pkce = await OAuthHelper.generatePKCE();
const url = OAuthHelper.buildAuthUrl({
clientId: process.env.OAUTH_CLIENT_ID!,
redirectUri: 'http://127.0.0.1:8080/callback',
scopes: ['account:profile'],
}, state, pkce);
// 2) Exchange code
const token = await OAuthHelper.exchangeCodeForToken({
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET, // confidential only; sent via client_secret_post
redirectUri: 'http://127.0.0.1:8080/callback',
scopes: ['account:profile'],
}, '<authorization_code>', pkce.codeVerifier);
// 3) Client credentials (service scopes)
const svcToken = await OAuthHelper.getClientCredentialsToken({
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
scopes: ['service:leagues', 'service:leagues:ladder'],
});
Scope Guidance
- Use
account:*scopes for user login (Authorization Code + PKCE). These authorize actions and reads tied to the signed-in PoE account. - Use
service:*scopes only with the Client Credentials grant and only for confidential clients (server-side). These are for server-to-server use cases and are not issued via user login. - Do not request
service:*scopes in the browser or during the user login flow; the token endpoint will reject or ignore them. - Keep user and service tokens separate. Acquire an
account:*token via Authorization Code for end-user actions, and a separateservice:*token via Client Credentials for backend tasks.
User‑Agent
User-Agent: OAuth {clientId}/{version} (contact: {email}) OptionalInfo
- You can (and should) include this in OAuth token requests. Pass
userAgentin theOAuthHelperconfig when callingexchangeCodeForToken,refreshToken, orgetClientCredentialsTokenfrom a server runtime (Node/Next.js API route). Browsers forbid setting this header and it will be ignored there.
Example (Node/Next.js route):
const tokens = await OAuthHelper.exchangeCodeForToken({
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
redirectUri: process.env.OAUTH_REDIRECT_URI!,
scopes: ['account:profile'],
userAgent: process.env.USER_AGENT!,
}, code, pkce.codeVerifier);
const refreshed = await OAuthHelper.refreshToken({
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
userAgent: process.env.USER_AGENT!,
}, refreshToken);
const svcToken = await OAuthHelper.getClientCredentialsToken({
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
scopes: ['service:leagues'],
userAgent: process.env.USER_AGENT!,
});
Third‑Party Notice
This product isn't affiliated with or endorsed by Grinding Gear Games in any way.