| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- /*
- * @copyright
- * Copyright © Microsoft Open Technologies, Inc.
- *
- * All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http: *www.apache.org/licenses/LICENSE-2.0
- *
- * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
- * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
- * ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A
- * PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
- *
- * See the Apache License, Version 2.0 for the specific language
- * governing permissions and limitations under the License.
- */
- 'use strict';
- var jwtConstants = require('./constants').Jwt;
- var Logger = require('./log').Logger;
- var util = require('./util');
- require('date-utils');
- var jws = require('jws');
- var uuid = require('uuid');
- /**
- * JavaScript dates are in milliseconds, but JWT dates are in seconds.
- * This function does the conversion.
- * @param {Date} date
- * @return {string}
- */
- function dateGetTimeInSeconds(date) {
- return Math.floor(date.getTime()/1000);
- }
- /**
- * Constructs a new SelfSignedJwt object.
- * @param {object} callContext Context specific to this token request.
- * @param {Authority} authority The authority to be used as the JWT audience.
- * @param {string} clientId The client id of the calling app.
- */
- function SelfSignedJwt(callContext, authority, clientId) {
- this._log = new Logger('SelfSignedJwt', callContext._logContext);
- this._callContext = callContext;
- this._authority = authority;
- this._tokenEndpoint = authority.tokenEndpoint;
- this._clientId = clientId;
- }
- /**
- * This wraps date creation in order to make unit testing easier.
- * @return {Date}
- */
- SelfSignedJwt.prototype._getDateNow = function() {
- return new Date();
- };
- SelfSignedJwt.prototype._getNewJwtId = function() {
- return uuid.v4();
- };
- /**
- * A regular certificate thumbprint is a hex encode string of the binary certificate
- * hash. For some reason teh x5t value in a JWT is a url save base64 encoded string
- * instead. This function does the conversion.
- * @param {string} thumbprint A hex encoded certificate thumbprint.
- * @return {string} A url safe base64 encoded certificate thumbprint.
- */
- SelfSignedJwt.prototype._createx5tValue = function(thumbprint) {
- var hexString = thumbprint.replace(/:/g, '').replace(/ /g, '');
- var base64 = (new Buffer(hexString, 'hex')).toString('base64');
- return util.convertRegularToUrlSafeBase64EncodedString(base64);
- };
- /**
- * Creates the JWT header.
- * @param {string} thumbprint A hex encoded certificate thumbprint.
- * @return {object}
- */
- SelfSignedJwt.prototype._createHeader = function(thumbprint) {
- var x5t = this._createx5tValue(thumbprint);
- var header = { typ: 'JWT', alg: 'RS256', x5t : x5t };
- this._log.verbose('Creating self signed JWT header');
- this._log.verbose('Creating self signed JWT header. x5t: ' + x5t, true);
- return header;
- };
- /**
- * Creates the JWT payload.
- * @return {object}
- */
- SelfSignedJwt.prototype._createPayload = function() {
- var now = this._getDateNow();
- var expires = (new Date(now.getTime())).addMinutes(jwtConstants.SELF_SIGNED_JWT_LIFETIME);
- this._log.verbose('Creating self signed JWT payload. Expires: ' + expires + ' NotBefore: ' + now);
- var jwtPayload = {};
- jwtPayload[jwtConstants.AUDIENCE] = this._tokenEndpoint;
- jwtPayload[jwtConstants.ISSUER] = this._clientId;
- jwtPayload[jwtConstants.SUBJECT] = this._clientId;
- jwtPayload[jwtConstants.NOT_BEFORE] = dateGetTimeInSeconds(now);
- jwtPayload[jwtConstants.EXPIRES_ON] = dateGetTimeInSeconds(expires);
- jwtPayload[jwtConstants.JWT_ID] = this._getNewJwtId();
- return jwtPayload;
- };
- SelfSignedJwt.prototype._throwOnInvalidJwtSignature = function(jwt) {
- var jwtSegments = jwt.split('.');
- if (3 > jwtSegments.length || !jwtSegments[2]) {
- throw this._log.createError('Failed to sign JWT. This is most likely due to an invalid certificate.');
- }
- return;
- };
- SelfSignedJwt.prototype._signJwt = function(header, payload, certificate) {
- var jwt;
- try {
- jwt = jws.sign({ header : header, payload : payload, secret : certificate });
- }
- catch (err) {
- this._log.error(err, true);
- throw this._log.createError('Failed to sign JWT.This is most likely due to an invalid certificate.');
- }
-
- this._throwOnInvalidJwtSignature(jwt);
- return jwt;
- };
- SelfSignedJwt.prototype._reduceThumbprint = function(thumbprint) {
- var canonical = thumbprint.toLowerCase().replace(/ /g, '').replace(/:/g, '');
- this._throwOnInvalidThumbprint(canonical);
- return canonical;
- };
- var numCharIn128BitHexString = 128/8*2;
- var numCharIn160BitHexString = 160/8*2;
- var thumbprintSizes = {};
- thumbprintSizes[numCharIn128BitHexString] = true;
- thumbprintSizes[numCharIn160BitHexString] = true;
- var thumbprintRegExp = /^[a-f\d]*$/;
- SelfSignedJwt.prototype._throwOnInvalidThumbprint = function(thumbprint) {
- if (!thumbprintSizes[thumbprint.length] || !thumbprintRegExp.test(thumbprint)) {
- throw this._log.createError('The thumbprint does not match a known format');
- }
- };
- /**
- * Creates a self signed JWT that can be used as a client_assertion.
- * @param {string} certificate A PEM encoded certificate private key.
- * @param {string} thumbprint A hex encoded thumbprint of the certificate.
- * @return {string} A self signed JWT token.
- */
- SelfSignedJwt.prototype.create = function(certificate, thumbprint) {
- thumbprint = this._reduceThumbprint(thumbprint);
- var header = this._createHeader(thumbprint);
- var payload = this._createPayload();
- var jwt = this._signJwt(header, payload, certificate);
- return jwt;
- };
- module.exports = SelfSignedJwt;
|