Wenme OAuth 2.1 Integration
Implement enterprise-grade passwordless authentication in minutes with our secure OAuth 2.1 platform. Full PKCE support, OpenID Connect, and zero passwords.
Documentation
Quick Start
Prerequisites
Integration Code
// 1. Redirect users to Wenme OAuth authorization
const authUrl = new URL('https://identity.wenme.net/oauth/authorize');
authUrl.searchParams.append('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.append('redirect_uri', 'YOUR_CALLBACK_URL');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', 'openid profile email');
authUrl.searchParams.append('state', generateRandomState());
// 2. PKCE (mandatory in OAuth 2.1)
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
authUrl.searchParams.append('code_challenge', codeChallenge);
authUrl.searchParams.append('code_challenge_method', 'S256');
window.location.href = authUrl.toString();Complete OAuth 2.1 Integration Guide
Step 1: Automatic Endpoint Discovery (Recommended)
Wenme supports OpenID Connect Discovery. Simply fetch this endpoint to get all OAuth configuration:
GET https://wenme.net/.well-known/openid-configurationReturns all endpoints, supported scopes, and algorithms in JSON. Use this for automatic configuration.
Step 2: Configure Your OAuth Client
For NextAuth.js or similar libraries:
{
id: "wenme",
name: "Wenme",
type: "oauth",
wellKnown: "https://wenme.net/.well-known/openid-configuration",
authorization: {
params: {
scope: "openid profile email"
}
},
clientId: process.env.WENME_CLIENT_ID,
clientSecret: process.env.WENME_CLIENT_SECRET,
idToken: true,
profile(profile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture
}
}
}OAuth Endpoints (All Return JSON)
https://identity.wenme.net/oauth/authorizehttps://wenme.net/oauth/tokenhttps://wenme.net/oauth/userinfohttps://identity.wenme.net/.well-known/jwks.jsonhttps://identity.wenme.net/oauth/revokehttps://identity.wenme.net/oauth/introspecthttps://identity.wenme.net/oauth/end_sessionOAuth Authorization URL Format
Complete Authorization URL Structure
All parameters are REQUIRED except where noted. Missing response_type=code will result in an error.
https://identity.wenme.net/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_CALLBACK_URL&
response_type=code&
scope=openid+profile+email&
state=RANDOM_STATE&
code_challenge=PKCE_CHALLENGE&
code_challenge_method=S256โ Required Parameters
client_idYour application's client IDredirect_uriMust match registered URI exactlyresponse_typeMust be "code" (OAuth 2.1)scopeSpace-separated permissionsstateRandom string for CSRF protectioncode_challengePKCE challenge (base64url)code_challenge_methodMust be "S256"๐ Available Scopes
openidOpenID Connect authenticationprofileUser's profile informationemailUser's email addressoffline_accessRefresh token for long-lived accessNote: PKCE is mandatory in OAuth 2.1. The implicit grant flow is not supported.
Setup Your Application
Register Your Application
Visit the Organization Dashboard to create your OAuth application.
Store Credentials Securely
Never expose your Client Secret in client-side code!
# .env file (server-side only)WENME_CLIENT_ID=wenme_abc123... WENME_CLIENT_SECRET=secret_xyz789... WENME_REDIRECT_URI=https://yourapp.com/auth/callback
OAuth 2.1 Flow
Full OAuth 2.1 Compliance (RFC 9207)
Wenme implements the latest OAuth 2.1 standard with mandatory security enhancements:
Authorization Request
Redirect user to Wenme authorization endpoint with PKCE parameters.
// 1. Redirect users to Wenme OAuth authorization
const authUrl = new URL('https://identity.wenme.net/oauth/authorize');
authUrl.searchParams.append('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.append('redirect_uri', 'YOUR_CALLBACK_URL');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', 'openid profile email');
authUrl.searchParams.append('state', generateRandomState());
// 2. PKCE (mandatory in OAuth 2.1)
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
authUrl.searchParams.append('code_challenge', codeChallenge);
authUrl.searchParams.append('code_challenge_method', 'S256');
window.location.href = authUrl.toString();Token Exchange
Exchange authorization code for access tokens.
// 3. Exchange authorization code for tokens
const response = await fetch('https://wenme.net/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
code: authorizationCode,
redirect_uri: 'YOUR_CALLBACK_URL',
code_verifier: codeVerifier // PKCE verifier (mandatory in OAuth 2.1)
})
});
const tokens = await response.json();
// tokens.access_token, tokens.id_token, tokens.refresh_tokenGet User Information
Use access token to fetch user profile.
// 4. Get user information
const userResponse = await fetch('https://identity.wenme.net/oauth/userinfo', {
headers: {
'Authorization': `Bearer ${tokens.access_token}`
}
});
const user = await userResponse.json();
// user.sub, user.email, user.name, user.picture (avatar URL)PKCE Security
Important Security Notice
PKCE (Proof Key for Code Exchange) is mandatory in OAuth 2.1 for ALL clients, including confidential clients. This prevents authorization code interception attacks and is no longer optional.
PKCE Implementation
// Generate code verifier (43-128 characters)
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return btoa(String.fromCharCode(...array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
// Generate code challenge from verifier
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}Code Examples
JavaScript Quick Start
// 1. Redirect users to Wenme OAuth authorization
const authUrl = new URL('https://identity.wenme.net/oauth/authorize');
authUrl.searchParams.append('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.append('redirect_uri', 'YOUR_CALLBACK_URL');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', 'openid profile email');
authUrl.searchParams.append('state', generateRandomState());
// 2. PKCE (mandatory in OAuth 2.1)
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
authUrl.searchParams.append('code_challenge', codeChallenge);
authUrl.searchParams.append('code_challenge_method', 'S256');
window.location.href = authUrl.toString();
// 3. Exchange authorization code for tokens
const response = await fetch('https://wenme.net/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
code: authorizationCode,
redirect_uri: 'YOUR_CALLBACK_URL',
code_verifier: codeVerifier // PKCE verifier (mandatory in OAuth 2.1)
})
});
const tokens = await response.json();
// tokens.access_token, tokens.id_token, tokens.refresh_token
// 4. Get user information
const userResponse = await fetch('https://identity.wenme.net/oauth/userinfo', {
headers: {
'Authorization': `Bearer ${tokens.access_token}`
}
});
const user = await userResponse.json();
// user.sub, user.email, user.name, user.picture (avatar URL)Logout & Token Management
Three Ways to Handle Logout
1. Token Revocation (Recommended)
Immediately invalidate tokens on the authorization server. Best for security.
POST https://identity.wenme.net/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=ACCESS_TOKEN&
token_type_hint=access_token&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET2. End Session (OpenID Connect)
Logs user out from Wenme and optionally redirects back to your app.
GET https://identity.wenme.net/oauth/end_session?
id_token_hint=ID_TOKEN&
post_logout_redirect_uri=https://yourapp.com/logout-complete&
state=xyz1233. Client-Side Only
Simply delete tokens from local storage. Token remains valid until expiry.
// JavaScript
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('refresh_token');
// Redirect to login or home
window.location.href = '/';Logout Implementation Examples
NextAuth.js with Token Revocation
// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth"
export default NextAuth({
// ... provider config
events: {
async signOut({ token }) {
// Revoke token when user signs out
if (token?.accessToken) {
await fetch('https://identity.wenme.net/oauth/revoke', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
token: token.accessToken,
token_type_hint: 'access_token',
client_id: process.env.WENME_CLIENT_ID,
client_secret: process.env.WENME_CLIENT_SECRET,
}),
});
}
},
},
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token
token.idToken = account.id_token
}
return token
},
},
})Express.js with End Session
// Express logout route
app.post('/logout', async (req, res) => {
const { id_token } = req.session;
// Clear server session
req.session.destroy();
// Build Wenme logout URL
const logoutUrl = new URL('https://identity.wenme.net/oauth/end_session');
logoutUrl.searchParams.append('id_token_hint', id_token);
logoutUrl.searchParams.append(
'post_logout_redirect_uri',
'https://yourapp.com/logout-complete'
);
logoutUrl.searchParams.append('state', generateRandomState());
// Redirect to Wenme logout
res.redirect(logoutUrl.toString());
});
// Logout complete callback
app.get('/logout-complete', (req, res) => {
// Verify state parameter
if (req.query.state !== expectedState) {
return res.status(400).send('Invalid state');
}
res.redirect('/');
});React SPA with Token Revocation
// React logout hook
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
export function useLogout() {
const navigate = useNavigate();
const logout = useCallback(async () => {
const token = localStorage.getItem('access_token');
if (token) {
// Revoke token
try {
await fetch('https://identity.wenme.net/oauth/revoke', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
token,
token_type_hint: 'access_token',
client_id: process.env.REACT_APP_WENME_CLIENT_ID,
// Note: Don't include client_secret in frontend apps
}),
});
} catch (error) {
console.error('Token revocation failed:', error);
}
}
// Clear local storage
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('refresh_token');
// Redirect to login
navigate('/login');
}, [navigate]);
return logout;
}Token Introspection
Check if a token is still valid without making an API call.
POST https://identity.wenme.net/oauth/introspect
Content-Type: application/x-www-form-urlencoded
token=ACCESS_TOKEN&
token_type_hint=access_token&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRETResponse (Active Token):
{
"active": true,
"scope": "openid profile email",
"client_id": "your_client_id",
"username": "user@example.com",
"exp": 1640995200
}Response (Inactive/Invalid Token):
{
"active": false
}API Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /oauth/authorize | GET | OAuth authorization endpoint |
| /oauth/token | POST | Exchange code for tokens |
| /api/user/profile | GET | Get user information |
| /oauth/revoke | POST | Revoke access token |
| /.well-known/openid-configuration | GET | OpenID Connect discovery |
| /.well-known/jwks.json | GET | JSON Web Key Set |
OAuth Scopes
| Scope | Description | Claims Returned |
|---|---|---|
| openid | Required for OIDC | sub |
| profile | User profile information | name, picture, given_name, family_name, preferred_username, updated_at |
| User email address | email, email_verified | |
| phone | User phone number | phone_number, phone_number_verified |
| address | User address | address (structured object) |
| offline_access | Refresh token | Enables refresh_token in token response |
Security Note: External applications only receive standard OIDC claims. Internal platform data (roles, permissions, tenant IDs) is never exposed to third-party applications.
Domain Configuration
App Domain Feature
Configure your application domain to automatically manage redirect URIs. Set your app domain once, and all standard OAuth callback paths will be configured.
โข https://newsforge.news/callback
โข https://newsforge.news/auth/callback
โข https://newsforge.news/oauth/callback
โข https://newsforge.news/api/auth/callback
โข https://newsforge.news/signin-callback
CNAME Support
Wenme OAuth fully supports CNAME records for custom domains. Use subdomains that point to your actual servers while maintaining a consistent brand.
Example CNAME Setup:
api.newsforge.news CNAME newsforge-api.aws.com
https://api.newsforge.news/oauth/callback โ
SSL Certificate Required
Ensure valid SSL certificates for all CNAME domains
Use CNAME Domain in URIs
Register redirect URIs with the CNAME domain, not the target
Multiple Variations Supported
Register all domain variations (www, subdomains, etc.)
Security Best Practices
OAuth 2.1 Security Requirements
Always use PKCE
PKCE is mandatory for all OAuth 2.1 flows, even for confidential clients
Validate state parameter
Always verify the state parameter to prevent CSRF attacks
Use HTTPS everywhere
All redirect URIs must use HTTPS in production (localhost allowed for development)
Secure token storage
Never store tokens in localStorage or sessionStorage. Use secure HTTP-only cookies or server-side storage
Rotate refresh tokens
Always exchange refresh tokens for new ones to detect token replay attacks
Validate redirect URIs
Register all redirect URIs and use exact string matching (no wildcards)
Common Security Mistakes to Avoid
- โ Using implicit flow (removed in OAuth 2.1)
- โ Storing client secrets in frontend code
- โ Not validating the state parameter
- โ Using localStorage for token storage
- โ Not implementing PKCE
- โ Using wildcard redirect URIs
- โ Not using HTTPS in production
- โ Long-lived access tokens (use short expiry + refresh tokens)
Testing Your Integration
Test Credentials
You can create a test application in the dashboard for development purposes.
Go to Organization DashboardOAuth Flow Tester
Use our built-in OAuth flow tester to validate your configuration.
Need Help?
Developer Support
Our team is here to help you integrate Wenme authentication into your applications.