1
+ /*
2
+ * @copyright
3
+ * Copyright © Microsoft Open Technologies, Inc.
4
+ *
5
+ * All Rights Reserved
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http: *www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
14
+ * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
15
+ * ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A
16
+ * PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
17
+ *
18
+ * See the Apache License, Version 2.0 for the specific language
19
+ * governing permissions and limitations under the License.
20
+ */
21
+ 'use strict' ;
22
+
23
+ var jwtConstants = require ( './constants' ) . Jwt ;
24
+ var Logger = require ( './log' ) . Logger ;
25
+ var util = require ( './util' ) ;
26
+
27
+ require ( 'date-utils' ) ;
28
+ var jws = require ( 'jws' ) ;
29
+ var uuid = require ( 'node-uuid' ) ;
30
+
31
+ /**
32
+ * JavaScript dates are in milliseconds, but JWT dates are in seconds.
33
+ * This function does the conversion.
34
+ * @param {Date } date
35
+ * @return {string }
36
+ */
37
+ function dateGetTimeInSeconds ( date ) {
38
+ return Math . floor ( date . getTime ( ) / 1000 ) ;
39
+ }
40
+
41
+ /**
42
+ * Constructs a new SelfSignedJwt object.
43
+ * @param {object } callContext Context specific to this token request.
44
+ * @param {Authority } authority The authority to be used as the JWT audience.
45
+ * @param {string } clientId The client id of the calling app.
46
+ */
47
+ function SelfSignedJwt ( callContext , authority , clientId ) {
48
+ this . _log = new Logger ( 'SelfSignedJwt' , callContext . _logContext ) ;
49
+ this . _callContext = callContext ;
50
+
51
+ this . _authority = authority ;
52
+ this . _tokenEndpoint = authority . tokenEndpoint ;
53
+ this . _clientId = clientId ;
54
+ }
55
+
56
+ /**
57
+ * This wraps date creation in order to make unit testing easier.
58
+ * @return {Date }
59
+ */
60
+ SelfSignedJwt . prototype . _getDateNow = function ( ) {
61
+ return new Date ( ) ;
62
+ } ;
63
+
64
+ SelfSignedJwt . prototype . _getNewJwtId = function ( ) {
65
+ return uuid . v4 ( ) ;
66
+ } ;
67
+
68
+ /**
69
+ * A regular certificate thumbprint is a hex encode string of the binary certificate
70
+ * hash. For some reason teh x5t value in a JWT is a url save base64 encoded string
71
+ * instead. This function does the conversion.
72
+ * @param {string } thumbprint A hex encoded certificate thumbprint.
73
+ * @return {string } A url safe base64 encoded certificate thumbprint.
74
+ */
75
+ SelfSignedJwt . prototype . _createx5tValue = function ( thumbprint ) {
76
+ var hexString = thumbprint . replace ( / : / g, '' ) . replace ( / / g, '' ) ;
77
+ var base64 = ( new Buffer ( hexString , 'hex' ) ) . toString ( 'base64' ) ;
78
+ return util . convertRegularToUrlSafeBase64EncodedString ( base64 ) ;
79
+ } ;
80
+
81
+ /**
82
+ * Creates the JWT header.
83
+ * @param {string } thumbprint A hex encoded certificate thumbprint.
84
+ * @return {object }
85
+ */
86
+ SelfSignedJwt . prototype . _createHeader = function ( thumbprint ) {
87
+ var x5t = this . _createx5tValue ( thumbprint ) ;
88
+ var header = { typ : 'JWT' , alg : 'RS256' , x5t : x5t } ;
89
+
90
+ this . _log . verbose ( 'Creating self signed JWT header. x5t: ' + x5t ) ;
91
+
92
+ return header ;
93
+ } ;
94
+
95
+ /**
96
+ * Creates the JWT payload.
97
+ * @return {object }
98
+ */
99
+ SelfSignedJwt . prototype . _createPayload = function ( ) {
100
+ var now = this . _getDateNow ( ) ;
101
+ var expires = ( new Date ( now . getTime ( ) ) ) . addMinutes ( jwtConstants . SELF_SIGNED_JWT_LIFETIME ) ;
102
+
103
+ this . _log . verbose ( 'Creating self signed JWT payload. Expires: ' + expires + ' NotBefore: ' + now ) ;
104
+
105
+ var jwtPayload = { } ;
106
+ jwtPayload [ jwtConstants . AUDIENCE ] = this . _tokenEndpoint ;
107
+ jwtPayload [ jwtConstants . ISSUER ] = this . _clientId ;
108
+ jwtPayload [ jwtConstants . SUBJECT ] = this . _clientId ;
109
+ jwtPayload [ jwtConstants . NOT_BEFORE ] = dateGetTimeInSeconds ( now ) ;
110
+ jwtPayload [ jwtConstants . EXPIRES_ON ] = dateGetTimeInSeconds ( expires ) ;
111
+ jwtPayload [ jwtConstants . JWT_ID ] = this . _getNewJwtId ( ) ;
112
+
113
+ return jwtPayload ;
114
+ } ;
115
+
116
+ SelfSignedJwt . prototype . _throwOnInvalidJwtSignature = function ( jwt ) {
117
+ var jwtSegments = jwt . split ( '.' ) ;
118
+
119
+ if ( 3 > jwtSegments . length || ! jwtSegments [ 2 ] ) {
120
+ throw this . _log . createError ( 'Failed to sign JWT. This is most likely due to an invalid certificate.' ) ;
121
+ }
122
+
123
+ return ;
124
+ } ;
125
+
126
+ SelfSignedJwt . prototype . _signJwt = function ( header , payload , certificate ) {
127
+ var jwt = jws . sign ( { header : header , payload : payload , secret : certificate } ) ;
128
+ this . _throwOnInvalidJwtSignature ( jwt ) ;
129
+ return jwt ;
130
+ } ;
131
+
132
+ SelfSignedJwt . prototype . _reduceThumbprint = function ( thumbprint ) {
133
+ var canonical = thumbprint . toLowerCase ( ) . replace ( / / g, '' ) . replace ( / : / g, '' ) ;
134
+ this . _throwOnInvalidThumbprint ( canonical ) ;
135
+ return canonical ;
136
+ } ;
137
+
138
+ var numCharIn128BitHexString = 128 / 8 * 2 ;
139
+ var numCharIn160BitHexString = 160 / 8 * 2 ;
140
+ var thumbprintSizes = { } ;
141
+ thumbprintSizes [ numCharIn128BitHexString ] = true ;
142
+ thumbprintSizes [ numCharIn160BitHexString ] = true ;
143
+ var thumbprintRegExp = / ^ [ a - f \d ] * $ / ;
144
+
145
+ SelfSignedJwt . prototype . _throwOnInvalidThumbprint = function ( thumbprint ) {
146
+ if ( ! thumbprintSizes [ thumbprint . length ] || ! thumbprintRegExp . test ( thumbprint ) ) {
147
+ throw this . _log . createError ( 'The thumbprint does not match a known format' ) ;
148
+ }
149
+ } ;
150
+
151
+ /**
152
+ * Creates a self signed JWT that can be used as a client_assertion.
153
+ * @param {string } certificate A PEM encoded certificate private key.
154
+ * @param {string } thumbprint A hex encoded thumbprint of the certificate.
155
+ * @return {string } A self signed JWT token.
156
+ */
157
+ SelfSignedJwt . prototype . create = function ( certificate , thumbprint ) {
158
+ thumbprint = this . _reduceThumbprint ( thumbprint ) ;
159
+ var header = this . _createHeader ( thumbprint ) ;
160
+
161
+ var payload = this . _createPayload ( ) ;
162
+
163
+ var jwt = this . _signJwt ( header , payload , certificate ) ;
164
+ return jwt ;
165
+ } ;
166
+
167
+ module . exports = SelfSignedJwt ;
0 commit comments