azurite
Version:
An open source Azure Storage API compatible server
163 lines • 7.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const jsonwebtoken_1 = require("jsonwebtoken");
const models_1 = require("../../common/models");
const constants_1 = require("../../common/utils/constants");
const QueueStorageContext_1 = tslib_1.__importDefault(require("../context/QueueStorageContext"));
const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory"));
const operation_1 = require("../generated/artifacts/operation");
const constants_2 = require("../utils/constants");
class QueueTokenAuthenticator {
constructor(dataStore, oauth, logger) {
this.dataStore = dataStore;
this.oauth = oauth;
this.logger = logger;
}
async validate(req, context) {
const queueContext = new QueueStorageContext_1.default(context);
const account = queueContext.account;
this.logger.info(`QueueTokenAuthenticator:validate() Start validation against token authentication.`, queueContext.contextID);
// TODO: Make following async
const accountProperties = this.dataStore.getAccount(account);
if (accountProperties === undefined) {
this.logger.error(`QueueTokenAuthenticator:validate() Invalid storage account ${account}.`, queueContext.contextID);
throw StorageErrorFactory_1.default.ResourceNotFound(context.contextID);
}
const authHeaderValue = req.getHeader(constants_2.HeaderConstants.AUTHORIZATION);
if (authHeaderValue === undefined) {
this.logger.info(`QueueTokenAuthenticator:validate() Request doesn't include valid authentication header. Skip token authentication.`, queueContext.contextID);
return;
}
if (queueContext.operation === operation_1.Operation.Queue_GetAccessPolicy ||
queueContext.operation === operation_1.Operation.Queue_SetAccessPolicy) {
this.logger.info(`QueueTokenAuthenticator:validate() Operation is not available with OAuth. Skip token authentication.`, queueContext.contextID);
return;
}
if (!authHeaderValue.startsWith(constants_1.BEARER_TOKEN_PREFIX)) {
throw StorageErrorFactory_1.default.getInvalidAuthenticationInfo(context.contextID);
}
if (req.getProtocol().toLowerCase() !== constants_1.HTTPS) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not allowed with HTTP.");
}
// TODO: Check API Version and enable bearer challenge after 2019-12-12
const token = authHeaderValue.substr(constants_1.BEARER_TOKEN_PREFIX.length + 1);
switch (this.oauth) {
case models_1.OAuthLevel.BASIC:
return this.authenticateBasic(token, context);
default:
this.logger.warn(`QueueTokenAuthenticator:validate() Unknown OAuth level ${this.oauth}. Skip token authentication.`, queueContext.contextID);
return;
}
}
async authenticateBasic(token, context) {
// tslint:disable: max-line-length
/**
* Example OAuth Bearer Token:
* {
* "aud": "https://storage.azure.com",
* "iss": "https://sts.windows-ppe.net/ab1f708d-50f6-404c-a006-d71b2ac7a606/",
* "iat": 1511859603,
* "nbf": 1511859603,
* "exp": 1511863503,
* "_claim_names": {
* "groups": "src1"
* },
* "_claim_sources": {
* "src1": {
* "endpoint": "https://graph.ppe.windows.net/ab1f708d-50f6-404c-a006-d71b2ac7a606/users/11059fcc-5514-4800-b5f8-49808b9cfab6/getMemberObjects"
* }
* },
* "acr": "1",
* "aio": "ASQA2/8JAAAAE9BLp+qWOO0iEg3n6mjPNqaoHSCIqhQQ8OCinJcub7U=",
* "amr": [
* "pwd"
* ],
* "appid": "f997392c-e15a-4ad8-af9e-cd6966caba7f",
* "appidacr": "0",
* "e_exp": 262800,
* "ipaddr": "167.220.255.51",
* "name": "test",
* "oid": "11059fcc-5514-4800-b5f8-49808b9cfab6",
* "puid": "10033FFFA6976143",
* "scp": "user_impersonation",
* "sub": "JeH2dBcxZp-_pKoskdxOilGj234LhlM_0GBM4JvSrJw",
* "tid": "ab1f708d-50f6-404c-a006-d71b2ac7a606",
* "unique_name": "test@oauthtest102.ccsctp.net",
* "upn": "test@oauthtest102.ccsctp.net",
* "uti": "dd77i7fCB0yn8aQYUqsJAA",
* "ver": "1.0"
* }
*/
// Validate JWT token format
let decoded;
try {
decoded = (0, jsonwebtoken_1.decode)(token);
}
catch {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported.");
}
if (!decoded) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported.");
}
// Validate signature, skip in basic check
// Validate nbf & exp
if (decoded.nbf === undefined ||
decoded.exp === undefined ||
decoded.iat === undefined) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported.");
}
const now = context.startTime.getTime();
const nbf = decoded.nbf * 1000;
const exp = decoded.exp * 1000;
if (now < nbf) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Lifetime validation failed.");
}
if (now > exp) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Lifetime validation failed. The token is expired.");
}
const iss = decoded.iss;
if (!iss) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported.");
}
let issMatch = false;
for (const validIssuePrefix of constants_1.VALID_ISSUE_PREFIXES) {
if (iss.startsWith(validIssuePrefix)) {
issMatch = true;
break;
}
}
if (!issMatch) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Invalid token issuer.");
}
const aud = decoded.aud;
if (!aud) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported.");
}
const queueContext = context;
let audMatch = false;
let m;
for (const regex of constants_2.VALID_QUEUE_AUDIENCES) {
m = regex.exec(aud);
if (m !== null) {
if (m[0] === aud) {
if (m[1] !== undefined && m[1] !== queueContext.account) {
// If account name doesn't match for fine-grained audience
break;
}
audMatch = true;
break;
}
}
}
if (!audMatch) {
throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Invalid token audience.");
}
// Skip validate scope
// Currently scope maybe null or user_impersonation
this.logger.info(`QueueTokenAuthenticator:authenticateBasic() Validation against token authentication successfully.`, queueContext.contextID);
return true;
}
}
exports.default = QueueTokenAuthenticator;
//# sourceMappingURL=QueueTokenAuthenticator.js.map
;