ltijs
Version:
Easily turn your web application into a LTI 1.3 Learning Tool.
84 lines • 3.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccessTokenManager = void 0;
const validation_1 = require("#utils/validation/validation");
const random_1 = require("#utils/random/random");
const jwt_1 = require("#utils/crypto/jwt");
const jwt_constants_1 = require("#utils/crypto/jwt.constants");
const access_token_manager_schemas_1 = require("#services/access-token-manager/access-token-manager.schemas");
const platform_keys_1 = require("#services/platform-manager/platform-keys");
class AccessTokenManager {
LOG_COMPONENT = 'accessTokenManager';
CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
GRANT_TYPE = 'client_credentials';
ACCEPT = 'application/json';
EXPIRY_SAFETY_MARGIN_SECONDS = 60;
databaseManager;
requestHandler;
logger;
constructor(databaseManager, requestHandler, logger) {
this.databaseManager = databaseManager;
this.requestHandler = requestHandler;
this.logger = logger;
}
async getAccessToken(platform, scopes) {
this.logger.debug(this.LOG_COMPONENT, `Attempting to retrieve access token for ${platform.url} with scopes: ${scopes}`);
const cached = await this.getCachedToken(platform, scopes);
const token = cached ?? (await this.requestAndCacheNewToken(platform, scopes));
return this.normalizeTokenType(token);
}
async getCachedToken(platform, scopes) {
const record = await this.databaseManager.getAccessToken(platform.url, platform.clientId, scopes);
const isExpired = record !== undefined &&
(Date.now() - record.createdAt) / 1000 > record.expires_in - this.EXPIRY_SAFETY_MARGIN_SECONDS;
if (record === undefined || isExpired)
return undefined;
this.logger.debug(this.LOG_COMPONENT, `Cached access token found for ${platform.url}`);
const { createdAt: _createdAt, ...token } = record;
return token;
}
async requestAndCacheNewToken(platform, scopes) {
const token = await this.requestNewToken(platform, scopes);
await this.cacheToken(platform, scopes, token);
this.logger.debug(this.LOG_COMPONENT, `Successfully generated new access token for ${platform.url} with scopes: ${scopes}`);
return token;
}
async requestNewToken(platform, scopes) {
const clientAssertion = await this.signClientAssertion(platform);
const body = new URLSearchParams({
grant_type: this.GRANT_TYPE,
client_assertion_type: this.CLIENT_ASSERTION_TYPE,
client_assertion: clientAssertion,
scope: scopes,
});
const response = await this.requestHandler.post(platform.accessTokenEndpoint, body, {
headers: { accept: this.ACCEPT },
});
return (0, validation_1.validate)(access_token_manager_schemas_1.AccessTokenSchema, response.data);
}
async signClientAssertion(platform) {
const assertion = {
sub: platform.clientId,
iss: platform.clientId,
aud: platform.authorizationServer,
jti: (0, random_1.randomJti)(),
};
return (0, jwt_1.signJwt)(assertion, (0, platform_keys_1.resolvePlatformPrivateKey)(platform), {
algorithm: jwt_constants_1.RS256_ALGORITHM,
expiresIn: 60,
keyid: platform.id,
});
}
async cacheToken(platform, scopes, token) {
await this.databaseManager.saveAccessToken(platform.url, platform.clientId, scopes, {
...token,
createdAt: Date.now(),
});
}
normalizeTokenType(token) {
return { ...token, token_type: token.token_type.charAt(0).toUpperCase() + token.token_type.slice(1) };
}
}
exports.AccessTokenManager = AccessTokenManager;
exports.default = AccessTokenManager;
//# sourceMappingURL=access-token-manager.service.js.map