@zestic/oauth-core
Version:
Framework-agnostic OAuth authentication library with support for multiple OAuth flows
184 lines • 6.47 kB
JavaScript
;
/**
* Token exchange and management
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenManager = void 0;
const OAuthTypes_1 = require("../types/OAuthTypes");
const ErrorHandler_1 = require("../utils/ErrorHandler");
class TokenManager {
constructor(httpAdapter, storageAdapter) {
this.httpAdapter = httpAdapter;
this.storageAdapter = storageAdapter;
}
/**
* Exchange authorization code for tokens
*/
async exchangeAuthorizationCode(code, codeVerifier, config) {
const request = {
grantType: 'authorization_code',
code,
redirectUri: config.redirectUri,
codeVerifier,
clientId: config.clientId,
};
return this.performTokenExchange(request, config);
}
/**
* Exchange magic link token for OAuth tokens
*/
async exchangeMagicLinkToken(token, config, additionalParams) {
const request = {
grantType: 'magic_link',
token,
clientId: config.clientId,
...additionalParams,
};
return this.performTokenExchange(request, config);
}
/**
* Refresh access token using refresh token
*/
async refreshToken(refreshToken, config) {
const request = {
grantType: 'refresh_token',
token: refreshToken,
clientId: config.clientId,
};
return this.performTokenExchange(request, config);
}
/**
* Perform the actual token exchange HTTP request
*/
async performTokenExchange(request, config) {
try {
const requestBody = this.buildTokenRequestBody(request);
const response = await this.httpAdapter.post(config.endpoints.token, requestBody, {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
});
if (response.status >= 400) {
throw ErrorHandler_1.ErrorHandler.handleTokenExchangeError(new Error(`HTTP ${response.status}`), response.data);
}
const tokenResponse = response.data;
// Store tokens
await this.storeTokens(tokenResponse);
return {
success: true,
accessToken: tokenResponse.access_token,
refreshToken: tokenResponse.refresh_token,
expiresIn: tokenResponse.expires_in,
};
}
catch (error) {
if (ErrorHandler_1.ErrorHandler.isOAuthError(error)) {
throw error;
}
throw ErrorHandler_1.ErrorHandler.handleTokenExchangeError(error instanceof Error ? error : new Error(String(error)));
}
}
/**
* Build the request body for token exchange
*/
buildTokenRequestBody(request) {
const body = {
grant_type: request.grantType,
client_id: request.clientId,
};
if (request.code) {
body.code = request.code;
}
if (request.redirectUri) {
body.redirect_uri = request.redirectUri;
}
if (request.codeVerifier) {
body.code_verifier = request.codeVerifier;
}
if (request.token) {
if (request.grantType === 'refresh_token') {
body.refresh_token = request.token;
}
else {
body.token = request.token;
}
}
if (request.state) {
body.state = request.state;
}
return body;
}
/**
* Store tokens in storage
*/
async storeTokens(tokenResponse) {
try {
await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.ACCESS_TOKEN, tokenResponse.access_token);
if (tokenResponse.refresh_token) {
await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.REFRESH_TOKEN, tokenResponse.refresh_token);
}
if (tokenResponse.expires_in) {
const expiryTime = Date.now() + (tokenResponse.expires_in * 1000);
await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.TOKEN_EXPIRY, expiryTime.toString());
}
await this.storageAdapter.setItem(TokenManager.STORAGE_KEYS.TOKEN_TYPE, tokenResponse.token_type || 'Bearer');
}
catch (error) {
throw ErrorHandler_1.ErrorHandler.createError('Failed to store tokens', OAuthTypes_1.OAUTH_ERROR_CODES.NETWORK_ERROR, error instanceof Error ? error : new Error(String(error)));
}
}
/**
* Get stored access token
*/
async getAccessToken() {
return this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.ACCESS_TOKEN);
}
/**
* Get stored refresh token
*/
async getRefreshToken() {
return this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.REFRESH_TOKEN);
}
/**
* Check if access token is expired
*/
async isTokenExpired() {
const expiryTimeStr = await this.storageAdapter.getItem(TokenManager.STORAGE_KEYS.TOKEN_EXPIRY);
if (!expiryTimeStr) {
return false; // No expiry time stored, assume not expired
}
const expiryTime = parseInt(expiryTimeStr, 10);
return Date.now() >= expiryTime;
}
/**
* Clear all stored tokens
*/
async clearTokens() {
const keys = Object.values(TokenManager.STORAGE_KEYS);
await this.storageAdapter.removeItems(keys);
}
/**
* Revoke tokens
*/
async revokeTokens(config) {
try {
const accessToken = await this.getAccessToken();
if (accessToken) {
await this.httpAdapter.post(config.endpoints.revocation, { token: accessToken, client_id: config.clientId }, { 'Content-Type': 'application/x-www-form-urlencoded' });
}
await this.clearTokens();
}
catch (error) {
// Log error but don't throw - token revocation failure shouldn't prevent logout
console.warn('Token revocation failed:', error);
await this.clearTokens();
}
}
}
exports.TokenManager = TokenManager;
TokenManager.STORAGE_KEYS = {
ACCESS_TOKEN: 'access_token',
REFRESH_TOKEN: 'refresh_token',
TOKEN_EXPIRY: 'token_expiry',
TOKEN_TYPE: 'token_type',
};
//# sourceMappingURL=TokenManager.js.map