@@ -5,6 +5,7 @@ var randomstring = require("randomstring");
55var cons = require ( 'consolidate' ) ;
66var nosql = require ( 'nosql' ) . load ( 'database.nosql' ) ;
77var querystring = require ( 'querystring' ) ;
8+ var qs = require ( 'qs' ) ;
89var __ = require ( 'underscore' ) ;
910__ . string = require ( 'underscore.string' ) ;
1011
@@ -26,6 +27,7 @@ var authServer = {
2627
2728// client information
2829var clients = [
30+
2931 {
3032 "client_id" : "oauth-client-1" ,
3133 "client_secret" : "oauth-client-secret-1" ,
@@ -48,11 +50,6 @@ app.get('/', function(req, res) {
4850
4951app . get ( "/authorize" , function ( req , res ) {
5052
51- /*
52- * Process the request, validate the client, and send the user to the approval page
53- */
54-
55-
5653 var client = getClient ( req . query . client_id ) ;
5754
5855 if ( ! client ) {
@@ -68,15 +65,13 @@ app.get("/authorize", function(req, res){
6865 var rscope = req . query . scope ? req . query . scope . split ( ' ' ) : undefined ;
6966 var cscope = client . scope ? client . scope . split ( ' ' ) : undefined ;
7067 if ( __ . difference ( rscope , cscope ) . length > 0 ) {
71- // client asked for a scope it couldn't have
72- var urlParsed = url . parse ( req . query . redirect_uri ) ;
73- delete urlParsed . search ; // this is a weird behavior of the URL library
74- urlParsed . query = urlParsed . query || { } ;
75- urlParsed . query . error = 'invalid_scope' ;
76- res . redirect ( url . format ( urlParsed ) ) ;
68+ var urlParsed = buildUrl ( req . query . redirect_uri , {
69+ error : 'invalid_scope'
70+ } ) ;
71+ res . redirect ( urlParsed ) ;
7772 return ;
7873 }
79-
74+
8075 var reqid = randomstring . generate ( 8 ) ;
8176
8277 requests [ reqid ] = req . query ;
@@ -102,90 +97,85 @@ app.post('/approve', function(req, res) {
10297 if ( req . body . approve ) {
10398 if ( query . response_type == 'code' ) {
10499 // user approved access
105- var code = randomstring . generate ( 8 ) ;
106-
107- var scope = __ . filter ( __ . keys ( req . body ) , function ( s ) { return __ . string . startsWith ( s , 'scope_' ) ; } )
108- . map ( function ( s ) { return s . slice ( 'scope_' . length ) ; } ) ;
100+
101+ var rscope = getScopesFromForm ( req . body ) ;
109102 var client = getClient ( query . client_id ) ;
110103 var cscope = client . scope ? client . scope . split ( ' ' ) : undefined ;
111- if ( __ . difference ( scope , cscope ) . length > 0 ) {
112- // client asked for a scope it couldn't have
113- var urlParsed = url . parse ( query . redirect_uri ) ;
114- delete urlParsed . search ; // this is a weird behavior of the URL library
115- urlParsed . query = urlParsed . query || { } ;
116- urlParsed . query . error = 'invalid_scope' ;
117- res . redirect ( url . format ( urlParsed ) ) ;
104+ if ( __ . difference ( rscope , cscope ) . length > 0 ) {
105+ var urlParsed = buildUrl ( query . redirect_uri , {
106+ error : 'invalid_scope'
107+ } ) ;
108+ res . redirect ( urlParsed ) ;
118109 return ;
119110 }
120111
112+ var code = randomstring . generate ( 8 ) ;
113+
121114 // save the code and request for later
122- codes [ code ] = { authorizationEndpointRequest : query , scope : scope } ;
115+
116+ codes [ code ] = { request : query , scope : rscope } ;
123117
124- var urlParsed = url . parse ( query . redirect_uri ) ;
125- delete urlParsed . search ; // this is a weird behavior of the URL library
126- urlParsed . query = urlParsed . query || { } ;
127- urlParsed . query . code = code ;
128- urlParsed . query . state = query . state ;
129- res . redirect ( url . format ( urlParsed ) ) ;
118+ var urlParsed = buildUrl ( query . redirect_uri , {
119+ code : code ,
120+ state : query . state
121+ } ) ;
122+ res . redirect ( urlParsed ) ;
130123 return ;
124+ } else if ( query . response_type == 'token' ) {
131125
132- } else if ( query . response_type == 'token' ) {
133- var scope = __ . filter ( __ . keys ( req . body ) , function ( s ) { return __ . string . startsWith ( s , 'scope_' ) ; } )
134- . map ( function ( s ) { return s . slice ( 'scope_' . length ) ; } ) ;
126+ var rscope = getScopesFromForm ( req . body ) ;
135127 var client = getClient ( query . client_id ) ;
136128 var cscope = client . scope ? client . scope . split ( ' ' ) : undefined ;
137- if ( __ . difference ( scope , cscope ) . length > 0 ) {
138- var urlParsed = url . parse ( query . redirect_uri ) ;
139- delete urlParsed . search ; // this is a weird behavior of the URL library
140- urlParsed . hash = 'error=invalid_scope' ;
141- res . redirect ( url . format ( urlParsed ) ) ;
129+ if ( __ . difference ( rscope , cscope ) . length > 0 ) {
130+ var urlParsed = buildUrl ( query . redirect_uri ,
131+ { } ,
132+ qs . stringify ( { error : 'invalid_scope' } )
133+ ) ;
134+ res . redirect ( urlParsed ) ;
142135 return ;
143136 }
144137 var access_token = randomstring . generate ( ) ;
145- nosql . insert ( { access_token : access_token , client_id : clientId , scope : scope } ) ;
146- var cscope = null ;
147- if ( scope ) {
148- cscope = scope . join ( ' ' ) ;
149- }
138+ nosql . insert ( { access_token : access_token , client_id : client . client_id , scope : rscope } ) ;
150139
151- var token_response = { access_token : access_token , token_type : 'Bearer' , scope : cscope } ;
152- var urlParsed = url . parse ( query . redirect_uri ) ;
153- delete urlParsed . search ; // this is a weird behavior of the URL library
140+ var token_response = { access_token : access_token , token_type : 'Bearer' , scope : rscope . join ( ' ' ) } ;
154141 if ( query . state ) {
155142 token_response . state = query . state ;
156- }
157- urlParsed . hash = qs . stringify ( token_response ) ;
158- res . redirect ( url . format ( urlParsed ) ) ;
143+ }
144+
145+ var urlParsed = buildUrl ( query . redirect_uri ,
146+ { } ,
147+ qs . stringify ( token_response )
148+ ) ;
149+ res . redirect ( urlParsed ) ;
159150 return ;
160151 } else {
161152 // we got a response type we don't understand
162- var urlParsed = url . parse ( query . redirect_uri ) ;
163- delete urlParsed . search ; // this is a weird behavior of the URL library
164- urlParsed . query = urlParsed . query || { } ;
165- urlParsed . query . error = 'unsupported_response_type' ;
166- res . redirect ( url . format ( urlParsed ) ) ;
153+ var urlParsed = buildUrl ( query . redirect_uri , {
154+ error : 'unsupported_response_type'
155+ } ) ;
156+ res . redirect ( urlParsed ) ;
167157 return ;
168158 }
159+
169160 } else {
170161 // user denied access
171- var urlParsed = url . parse ( query . redirect_uri ) ;
172- delete urlParsed . search ; // this is a weird behavior of the URL library
173- urlParsed . query = urlParsed . query || { } ;
174- urlParsed . query . error = 'access_denied' ;
175- res . redirect ( url . format ( urlParsed ) ) ;
162+ var urlParsed = buildUrl ( query . redirect_uri , {
163+ error : 'access_denied'
164+ } ) ;
165+ res . redirect ( urlParsed ) ;
176166 return ;
177167 }
178-
168+
179169} ) ;
180170
181171app . post ( "/token" , function ( req , res ) {
182-
172+
183173 var auth = req . headers [ 'authorization' ] ;
184174 if ( auth ) {
185175 // check the auth header
186- var clientCredentials = new Buffer ( auth . slice ( 'basic ' . length ) , 'base64' ) . toString ( ) . split ( ':' ) ;
187- var clientId = querystring . unescape ( clientCredentials [ 0 ] ) ;
188- var clientSecret = querystring . unescape ( clientCredentials [ 1 ] ) ;
176+ var clientCredentials = decodeClientCredentials ( auth ) ;
177+ var clientId = clientCredentials . id ;
178+ var clientSecret = clientCredentials . secret ;
189179 }
190180
191181 // otherwise, check the post body
@@ -220,62 +210,92 @@ app.post("/token", function(req, res){
220210
221211 if ( code ) {
222212 delete codes [ req . body . code ] ; // burn our code, it's been used
223- if ( code . authorizationEndpointRequest . client_id == clientId ) {
213+ if ( code . request . client_id == clientId ) {
224214
225215 var access_token = randomstring . generate ( ) ;
226216 var refresh_token = randomstring . generate ( ) ;
227217
228218 nosql . insert ( { access_token : access_token , client_id : clientId , scope : code . scope } ) ;
229219 nosql . insert ( { refresh_token : refresh_token , client_id : clientId , scope : code . scope } ) ;
230220
221+ console . log ( 'Issuing access token %s' , access_token ) ;
222+
231223 var token_response = { access_token : access_token , token_type : 'Bearer' , refresh_token : refresh_token , scope : code . scope . join ( ' ' ) } ;
232224
233225 res . status ( 200 ) . json ( token_response ) ;
234226 console . log ( 'Issued tokens for code %s' , req . body . code ) ;
235227
236228 return ;
237229 } else {
238- console . log ( 'Client mismatch, expected %s got %s' , code . authorizationEndpointRequest . client_id , clientId ) ;
230+ console . log ( 'Client mismatch, expected %s got %s' , code . request . client_id , clientId ) ;
239231 res . status ( 400 ) . json ( { error : 'invalid_grant' } ) ;
240232 return ;
241233 }
234+
235+
242236 } else {
243237 console . log ( 'Unknown code, %s' , req . body . code ) ;
244238 res . status ( 400 ) . json ( { error : 'invalid_grant' } ) ;
245239 return ;
246240 }
247241 } else if ( req . body . grant_type == 'refresh_token' ) {
248- nosql . all ( function ( token ) {
249- return ( token . refresh_token == req . body . refresh_token ) ;
250- } , function ( err , tokens ) {
251- if ( tokens . length == 1 ) {
252- var token = tokens [ 0 ] ;
242+ nosql . one ( function ( token ) {
243+ if ( token . refresh_token == req . body . refresh_token ) {
244+ return token ;
245+ }
246+ } , function ( err , token ) {
247+ if ( token ) {
248+ console . log ( "We found a matching refresh token: %s" , req . body . refresh_token ) ;
253249 if ( token . client_id != clientId ) {
254- console . log ( 'Invalid client using a refresh token, expected %s got %s' , token . client_id , clientId ) ;
255250 nosql . remove ( function ( found ) { return ( found == token ) ; } , function ( ) { } ) ;
256- res . status ( 400 ) . end ( ) ;
257- return
251+ res . status ( 400 ) . json ( { error : 'invalid_grant' } ) ;
252+ return ;
258253 }
259- console . log ( "We found a matching token: %s" , req . body . refresh_token ) ;
260254 var access_token = randomstring . generate ( ) ;
261- var token_response = { access_token : access_token , token_type : 'Bearer' , refresh_token : req . body . refresh_token } ;
262255 nosql . insert ( { access_token : access_token , client_id : clientId } ) ;
263- console . log ( 'Issuing access token %s for refresh token %s' , access_token , req . body . refresh_token ) ;
256+ var token_response = { access_token : access_token , token_type : 'Bearer' , refresh_token : token . refresh_token } ;
264257 res . status ( 200 ) . json ( token_response ) ;
265258 return ;
266259 } else {
267260 console . log ( 'No matching token was found.' ) ;
268- res . status ( 401 ) . end ( ) ;
261+ res . status ( 400 ) . json ( { error : 'invalid_grant' } ) ;
262+ return ;
269263 }
270264 } ) ;
271265 } else {
272266 console . log ( 'Unknown grant type %s' , req . body . grant_type ) ;
273267 res . status ( 400 ) . json ( { error : 'unsupported_grant_type' } ) ;
274- return ;
275268 }
276-
277269} ) ;
278270
271+ var buildUrl = function ( base , options , hash ) {
272+ var newUrl = url . parse ( base , true ) ;
273+ delete newUrl . search ;
274+ if ( ! newUrl . query ) {
275+ newUrl . query = { } ;
276+ }
277+ __ . each ( options , function ( value , key , list ) {
278+ newUrl . query [ key ] = value ;
279+ } ) ;
280+ if ( hash ) {
281+ newUrl . hash = hash ;
282+ }
283+
284+ return url . format ( newUrl ) ;
285+ } ;
286+
287+ var decodeClientCredentials = function ( auth ) {
288+ var clientCredentials = new Buffer ( auth . slice ( 'basic ' . length ) , 'base64' ) . toString ( ) . split ( ':' ) ;
289+ var clientId = querystring . unescape ( clientCredentials [ 0 ] ) ;
290+ var clientSecret = querystring . unescape ( clientCredentials [ 1 ] ) ;
291+ return { id : clientId , secret : clientSecret } ;
292+ } ;
293+
294+ var getScopesFromForm = function ( body ) {
295+ return __ . filter ( __ . keys ( body ) , function ( s ) { return __ . string . startsWith ( s , 'scope_' ) ; } )
296+ . map ( function ( s ) { return s . slice ( 'scope_' . length ) ; } ) ;
297+ } ;
298+
279299app . use ( '/' , express . static ( 'files/authorizationServer' ) ) ;
280300
281301// clear the database
0 commit comments