moc-oauth-client
v3.0.9
Published
OAuth client for Ministry of Commerce, Cambodia
Readme
moc-oauth-client
TypeScript SDK for integrating backend applications with the Ministry of Commerce Identity OAuth API.
Use this package from your server, API, or backend-for-frontend. Do not use it directly in browser code because it requires your OAuth client secret.
What It Does
- Creates an Identity login redirect URL
- Exchanges an authorization code for provider-issued tokens
- Validates an access token by looking up the user profile
- Refreshes provider-issued tokens
- Revokes a refresh token on logout
Installation
npm install moc-oauth-clientNode.js 18 or newer is required.
Configuration
You can configure the SDK with environment variables:
BASE_URL=https://identity-api-dev.moc.gov.kh
CLIENT_ID=your_client_id
CLIENT_SECRET=your_client_secret
REDIRECT_URI=https://your-app.example.com/callbackimport { MOCOAuthClient } from "moc-oauth-client";
const oauth = new MOCOAuthClient();Or pass configuration directly:
const oauth = new MOCOAuthClient({
baseUrl: "https://identity-api-dev.moc.gov.kh",
clientId: "your_client_id",
clientSecret: "your_client_secret",
redirectUri: "https://your-app.example.com/callback",
timeoutMs: 10000,
});OAuth Flow
- Generates an OAuth
statevalue and PKCEcodeVerifier. - Derives an S256
codeChallengefrom the verifier. - Stores
stateandcodeVerifierin its own secure session/cookie storage. - Call
getLoginToken({ state, codeChallenge }). - Redirect the user to
result.data.redirectUri. - The user signs in with MOC Identity.
- MOC Identity redirects back to your
REDIRECT_URIwithcodeandstate. - Verify the returned
state. - Call
validateAuthorizationCode({ code, codeVerifier }). - Store or session-manage the returned provider
accessTokenandrefreshToken. - Use
lookupUserProfile(accessToken)in protected API routes to validate the token. - Use
refreshToken(refreshToken)when the access token expires. - Use
logout(refreshToken)to revoke the session.
Response Shape
All methods return a result object. API failures are returned in error; they are not thrown.
type ApiResponse<T> =
| {
status: number;
success: true;
message?: string;
data: T;
error: null;
}
| {
status?: number;
success: false;
data: null;
error: {
code: string;
message: string;
status?: number;
};
};Recommended handling:
const result = await oauth.lookupUserProfile(accessToken);
if (!result.success) {
console.error(result.error.code, result.error.message);
return;
}
console.log(result.data.email);Methods
getLoginToken()
Creates a login URL for the configured OAuth client.
Example:
const result = await oauth.getLoginToken({
state,
codeChallenge,
codeChallengeMethod: "S256",
});
if (result.success) {
response.redirect(result.data.redirectUri);
}Success data:
{
"redirectUri": "https://identity.example.com/login?loginToken=..."
}validateAuthorizationCode({ code, codeVerifier })
Exchanges the authorization code for provider-issued tokens.
Example:
const result = await oauth.validateAuthorizationCode({
code,
codeVerifier,
});
if (!result.success || !result.data.isValid || !result.data.payload) {
throw new Error(result.error?.message ?? "Invalid authorization code");
}
const { accessToken, refreshToken, email } = result.data.payload;Success data:
{
"isValid": true,
"payload": {
"id": 1,
"email": "[email protected]",
"username": "[email protected]",
"position": "រដ្ឋលេខាធិការ",
"isActive": true,
"domain": "your-app.example.com",
"accessToken": "...",
"refreshToken": "..."
}
}lookupUserProfile(accessToken)
Validates an access token and returns the current user profile.
Example:
const result = await oauth.lookupUserProfile(accessToken);
if (!result.success) {
throw new Error(result.error.message);
}
return result.data;Success data:
{
"id": 1,
"email": "[email protected]",
"username": "[email protected]",
"position": "រដ្ឋលេខាធិការ",
"isActive": true
}refreshToken(refreshToken)
Exchanges a valid provider refresh token for a new token pair.
Example:
const result = await oauth.refreshToken(refreshToken);
if (result.success) {
session.accessToken = result.data.accessToken;
session.refreshToken = result.data.refreshToken;
}logout(refreshToken)
Revokes a provider refresh token.
Example:
await oauth.logout(refreshToken);Express Middleware Example
import type { Request, Response, NextFunction } from "express";
import { MOCOAuthClient } from "moc-oauth-client";
const oauth = new MOCOAuthClient();
export async function requireMocUser(
req: Request,
res: Response,
next: NextFunction,
) {
const authorization = req.headers.authorization;
const accessToken = authorization?.startsWith("Bearer ")
? authorization.slice(7)
: null;
if (!accessToken) {
return res.status(401).json({ message: "Missing bearer token" });
}
const result = await oauth.lookupUserProfile(accessToken);
if (!result.success || !result.data.isActive) {
return res.status(result.status ?? 401).json({ error: result.error });
}
req.user = result.data;
next();
}Compatibility Aliases
Older integrations can still use:
authorizeClient()as an alias ofgetLoginToken()getCurrentUser()as an alias oflookupUserProfile()
Prefer the newer method names in new code.
Security Notes
- Keep
CLIENT_SECRETonly on the server. - Store refresh tokens securely.
- Do not generate replacement tokens in your client API unless you have a specific token-exchange design.
- Validate access tokens through
lookupUserProfile()or a trusted local validation strategy that matches the Identity provider. - Use HTTPS for redirect URIs in production.
