-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathoauth2server.cfc
211 lines (187 loc) · 5.84 KB
/
oauth2server.cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
* @displayname oauth2server
* @output false
* @hint The oauth2server object.
* @author Matt Gifford
* @website https://www.monkehworks.com
* @purpose A ColdFusion Component to manage creation of OAuth2 access and refresh tokens
**/
component accessors="true" {
property name="secretKey" type="string";
property name="issuer" type="string";
property name="audience" type="string";
property name="oJWT" type="component";
property name="oPKCE" type="component";
property name="oHashID" type="component";
/**
* Constructor
*/
public function init(
required string secretKey,
required string issuer,
required string audience,
required string hashSalt,
numeric authCodeLength = 16
){
setSecretKey( arguments.secretKey );
setIssuer( arguments.issuer );
setAudience( arguments.audience );
setoJWT( createObject( 'component', 'utils.deps.jwt.cf_jwt' ).init(
secretkey = getSecretKey(),
issuer = arguments.issuer,
audience = arguments.audience
) );
setoPKCE( new utils.deps.pkce.pkce() );
setoHashID( new utils.Hashids( arguments.hashSalt, arguments.authCodeLength ) );
return this;
}
/**
* Generate the access token and refresh values
* @userId The user ID of the user this token is for
* @clientId The client ID of the app that is generating this token
* @issuer The issuer (the token endpoint)
* @scope An array of scopes this token is valid for
*/
function generateAccessToken(
required string userId,
required string clientId,
required string issuer,
array scope = []
){
var stuResponse = {};
var oJWT = getOJWT();
var dateIssued = now();
var dateExpires = dateAdd( 'n', 60, dateIssued );
var stuPayload = {
'sub' : arguments.userId, // Subject (The user ID)
'iss' : arguments.issuer, // Issuer (the token endpoint)
'aud' : arguments.clientId, // Audience (intended for use by the client that generated the token)
'iat' : createEpoch( dateIssued ), // Issued At
'exp' : createEpoch( dateExpires ), // Expires At
'scope': arrayToList( arguments.scope )
};
stuResponse[ 'access_token' ] = oJWT.encode( stuPayload );
stuResponse[ 'token_type' ] = 'bearer';
stuResponse[ 'expires_in' ] = 3600;
stuResponse[ 'refresh_token' ] = generateRefreshToken( argumentCollection = arguments );
stuResponse[ 'scope' ] = arrayToList( arguments.scope );
return stuResponse;
}
/**
* Generate a refresh token
* @userId The user ID of the user this token is for
* @clientId The client ID of the app that is generating this token
* @issuer The issuer (the token endpoint)
* @scope An array of scopes this token is valid for
*/
function generateRefreshToken(
required string userId,
required string clientId,
required string issuer,
array scope = []
){
var oJWT = getOJWT();
var dateIssued = now();
var dateExpires = dateAdd( 'n', 60, dateIssued );
var stuPayload = {
'sub' : arguments.userId, // Subject (The user ID)
'iss' : arguments.issuer, // Issuer (the token endpoint)
'aud' : arguments.clientId, // Audience (intended for use by the client that generated the token)
'iat' : createEpoch( dateIssued ), // Issued At
'scope': arrayToList( arguments.scope )
};
return oJWT.encode( stuPayload );
}
/**
* Generate a self-encoded authorization code
* @userId The numeric user ID of the user this token is for
* @clientId The numeric client ID of the app that is generating this token
*/
function generateAuthCode(
required numeric userId,
required numeric clientId,
string format = 'hash'
){
var arrData = [
arguments.clientId,
arguments.userId,
];
if( arguments.format == 'hash' ){
return encodeHash( arrData );
} else {
return generateJWTAuthCode( userId = arguments.userId, clientId = arguments.clientId );
}
}
private string function generateJWTAuthCode(
required numeric userId,
required numeric clientId
){
var oJWT = getOJWT();
var dateIssued = now();
var dateExpires = dateAdd( 's', 30, dateIssued );
var stuPayload = {
'sub': arguments.userId,
'aud': arguments.clientId,
'iat': createEpoch( dateIssued ),
'exp': createEpoch( dateExpires )
};
return oJWT.encode( stuPayload );
}
/**
* Encodes a unique hashID using the given array of numeric data
*
* @data The array of numeric data
*/
public string function encodeHash( required array data ){
return getoHashID().encode( arguments.data );
}
/**
* Decodes a unique hash and returns the array of numeric data
*
* @hashString The hash string to decode
*/
public array function decodeHash( required string hashString ){
return getoHashID().decode( arguments.hashString );
}
/**
* A shorthand to access the JWT decode method.
* @jwt The JWT to decode
*/
function decode( required string jwt ){
return getOJWT().decode( jwt );
}
/**
* Verify a challenge (can be used by an OAuth 2.0 server to verify)
*
* @codeVerifier The code verifier to use when verifying the code challenge
* @codeChallenge The code challenge to use when verifying
*/
public boolean function validatePKCE(
required string codeVerifier,
required string codeChallenge
){
return getOPKCE().verifyChallenge(
codeVerifier = arguments.codeVerifier,
codeChallenge = arguments.codeChallenge
);
}
/**
* Create epoch time
* @date The string date time value to convert
*/
function createEpoch( required string date ){
return dateDiff( 's', dateConvert( "utc2Local", "January 1 1970 00:00" ), arguments.date );
}
/**
* Returns the properties as a struct
*/
public struct function getMemento(){
var result = {};
for( var thisProp in getMetaData( this ).properties ){
if( structKeyExists( variables, thisProp[ 'name' ] ) ){
result[ thisProp[ 'name' ] ] = variables[ thisProp[ 'name' ] ];
}
}
return result;
}
}