Skip to content

Commit f8c2148

Browse files
added refresh_token grant type test
1 parent 4e1d8a8 commit f8c2148

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* Request an access token using the refresh token grant type.
3+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-6
4+
*
5+
* grant_type
6+
* REQUIRED. Value MUST be set to "refresh_token".
7+
* refresh_token
8+
* REQUIRED. The refresh token issued to the client.
9+
* scope
10+
* OPTIONAL. The scope of the access request as described by
11+
* Section 3.3. The requested scope MUST NOT include any scope
12+
* not originally granted by the resource owner, and if omitted is
13+
* treated as equal to the scope originally granted by the
14+
* resource owner.
15+
*/
16+
17+
18+
/**
19+
* Response
20+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
21+
*
22+
* access_token
23+
* REQUIRED. The access token issued by the authorization server.
24+
* token_type
25+
* REQUIRED. The type of the token issued as described in
26+
* Section 7.1. Value is case insensitive.
27+
* expires_in
28+
* RECOMMENDED. The lifetime in seconds of the access token. For
29+
* example, the value "3600" denotes that the access token will
30+
* expire in one hour from the time the response was generated.
31+
* If omitted, the authorization server SHOULD provide the
32+
* expiration time via other means or document the default value.
33+
* refresh_token
34+
* OPTIONAL. The refresh token, which can be used to obtain new
35+
* access tokens using the same authorization grant as described
36+
* in Section 6.
37+
* scope
38+
* OPTIONAL, if identical to the scope requested by the client;
39+
* otherwise, REQUIRED. The scope of the access token as
40+
* described by Section 3.3.
41+
*/
42+
43+
/**
44+
* Response (error)
45+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
46+
*
47+
* error
48+
* REQUIRED. A single ASCII [USASCII] error code from the following:
49+
* invalid_request, invalid_client, invalid_grant
50+
* unauthorized_client, unsupported_grant_type, invalid_scope
51+
* error_description
52+
* OPTIONAL. Human-readable ASCII [USASCII] text providing
53+
* additional information, used to assist the client developer in
54+
* understanding the error that occurred.
55+
* error_uri
56+
* OPTIONAL. A URI identifying a human-readable web page with
57+
* information about the error, used to provide the client
58+
* developer with additional information about the error.
59+
*/
60+
const OAuth2Server = require('../..');
61+
const DB = require('../helpers/db');
62+
const createModel = require('../helpers/model');
63+
const createRequest = require('../helpers/request');
64+
const Response = require('../../lib/response');
65+
66+
require('chai').should();
67+
68+
const db = new DB();
69+
70+
const auth = new OAuth2Server({
71+
model: createModel(db)
72+
});
73+
74+
const user = db.saveUser({ id: 1, username: 'test', password: 'test'});
75+
const client = db.saveClient({ id: 'a', secret: 'b', grants: ['password', 'refresh_token'] });
76+
const scope = 'read write';
77+
78+
function createLoginRequest () {
79+
return createRequest({
80+
body: {
81+
grant_type: 'password',
82+
client_id: client.id,
83+
client_secret: client.secret,
84+
username: user.username,
85+
password: user.password,
86+
scope
87+
},
88+
headers: {
89+
'content-type': 'application/x-www-form-urlencoded'
90+
},
91+
method: 'POST',
92+
});
93+
}
94+
95+
function createRefreshRequest (refresh_token) {
96+
return createRequest({
97+
method: 'POST',
98+
body: {
99+
grant_type: 'refresh_token',
100+
client_id: client.id,
101+
client_secret: client.secret,
102+
refresh_token,
103+
scope
104+
},
105+
headers: {
106+
'content-type': 'application/x-www-form-urlencoded'
107+
}
108+
});
109+
}
110+
111+
describe('RefreshTokenGrantType Compliance', function () {
112+
describe('With scope', function () {
113+
it('Should generate token response', async function () {
114+
const request = createLoginRequest();
115+
const response = new Response({});
116+
117+
const credentials = await auth.token(request, response, {});
118+
119+
const refreshRequest = createRefreshRequest(credentials.refreshToken);
120+
const refreshResponse = new Response({});
121+
122+
const token = await auth.token(refreshRequest, refreshResponse, {});
123+
124+
refreshResponse.body.token_type.should.equal('Bearer');
125+
refreshResponse.body.access_token.should.equal(token.accessToken);
126+
refreshResponse.body.refresh_token.should.equal(token.refreshToken);
127+
refreshResponse.body.expires_in.should.be.a('number');
128+
refreshResponse.body.scope.should.equal(scope);
129+
130+
token.accessToken.should.be.a('string');
131+
token.refreshToken.should.be.a('string');
132+
token.accessTokenExpiresAt.should.be.a('date');
133+
token.refreshTokenExpiresAt.should.be.a('date');
134+
token.scope.should.equal(scope);
135+
136+
db.accessTokens.has(token.accessToken).should.equal(true);
137+
db.refreshTokens.has(token.refreshToken).should.equal(true);
138+
});
139+
140+
it('Should throw invalid_grant error', async function () {
141+
const request = createRefreshRequest('invalid');
142+
const response = new Response({});
143+
144+
await auth.token(request, response, {})
145+
.then(() => {
146+
throw Error('Should not reach this');
147+
}).catch(err => {
148+
err.name.should.equal('invalid_grant');
149+
});
150+
});
151+
152+
// TODO: test refresh token with different scopes
153+
it('Should throw invalid_scope error', async function () {
154+
const request = createLoginRequest();
155+
const response = new Response({});
156+
157+
const credentials = await auth.token(request, response, {});
158+
159+
const refreshRequest = createRefreshRequest(credentials.refreshToken);
160+
const refreshResponse = new Response({});
161+
162+
refreshRequest.scope = 'invalid';
163+
164+
await auth.token(refreshRequest, refreshResponse, {})
165+
.then(() => {
166+
throw Error('Should not reach this');
167+
})
168+
.catch(err => {
169+
err.name.should.equal('invalid_scope');
170+
});
171+
});
172+
});
173+
});

0 commit comments

Comments
 (0)