Model Specification
Version >=5.x: Callback support has been removed! Each model function supports either sync or async (Promise
or async function
) return values.
Version <=4.x: Each model function supports promises, Node-style callbacks, ES6 generators and async/await (using Babel). Note that promise support implies support for returning plain values where asynchronism is not required.
const model = {
// We support returning promises.
getAccessToken: function() {
return new Promise('works!');
},
// Or sync-style values
getAuthorizationCode: function() {
return 'works!'
},
// Or, using generators.
getClient: function*() {
yield somethingAsync();
return 'works!';
},
// Or, async/wait (using Babel).
getUser: async function() {
await somethingAsync();
return 'works!';
}
};
const OAuth2Server = require('@node-oauth/oauth2-server');
let oauth = new OAuth2Server({model: model});
Code examples on this page use promises.
generateAccessToken(client, user, scope)
Invoked to generate a new access token.
This model function is optional. If not implemented, a default handler is used that generates access tokens consisting of 40 characters in the range of a..z0..9
.
Invoked during:
authorization_code
grantclient_credentials
grantrefresh_token
grantpassword
grant
Arguments:
Name |
Type |
Description |
---|---|---|
client |
Object |
The client the access token is generated for. |
user |
Object |
The user the access token is generated for. |
scope |
String[] |
The scopes associated with the access token. Can be |
Return value:
A String
to be used as access token.
RFC 6749 Appendix A.12 specifies that access tokens must consist of characters inside the range 0x20..0x7E
(i.e. only printable US-ASCII characters).
Remarks:
client
is the object previously obtained through Model#getClient().
user
is the user object previously obtained through Model#getAuthorizationCode() (code.user
; authorization code grant), Model#getUserFromClient() (client credentials grant), Model#getRefreshToken() (token.user
; refresh token grant) or Model#getUser() (password grant).
generateRefreshToken(client, user, scope)
Invoked to generate a new refresh token.
This model function is optional. If not implemented, a default handler is used that generates refresh tokens consisting of 40 characters in the range of a..z0..9
.
Invoked during:
authorization_code
grantrefresh_token
grantpassword
grant
Arguments:
Name |
Type |
Description |
---|---|---|
client |
Object |
The client the refresh token is generated for. |
user |
Object |
The user the refresh token is generated for. |
scope |
String[] |
The scopes associated with the refresh token. Can be |
Return value:
A String
to be used as refresh token.
RFC 6749 Appendix A.17 specifies that refresh tokens must consist of characters inside the range 0x20..0x7E
(i.e. only printable US-ASCII characters).
Remarks:
client
is the object previously obtained through Model#getClient().
user
is the user object previously obtained through Model#getAuthorizationCode() (code.user
; authorization code grant), Model#getRefreshToken() (token.user
; refresh token grant) or Model#getUser() (password grant).
getAccessToken(accessToken)
Invoked to retrieve an existing access token previously saved through Model#saveToken().
This model function is required if OAuth2Server#authenticate() is used.
Invoked during:
request authentication
Arguments:
Name |
Type |
Description |
---|---|---|
accessToken |
String |
The access token to retrieve. |
Return value:
An Object
representing the access token and associated data.
Name |
Type |
Description |
---|---|---|
token |
Object |
The return value. |
token.accessToken |
String |
The access token passed to |
token.accessTokenExpiresAt |
Date |
The expiry time of the access token. |
[token.scope] |
String[] |
The authorized scope of the access token. |
token.client |
Object |
The client associated with the access token. |
token.client.id |
String |
A unique string identifying the client. |
token.user |
Object |
The user associated with the access token. |
token.client
and token.user
can carry additional properties that will be ignored by oauth2-server.
Remarks:
function getAccessToken(accessToken) {
// imaginary DB queries
return db.queryAccessToken({access_token: accessToken})
.then(function(token) {
return Promise.all([
token,
db.queryClient({id: token.client_id}),
db.queryUser({id: token.user_id})
]);
})
.spread(function(token, client, user) {
return {
accessToken: token.access_token,
accessTokenExpiresAt: token.expires_at,
scope: token.scope,
client: client, // with 'id' property
user: user
};
});
}
getRefreshToken(refreshToken)
Invoked to retrieve an existing refresh token previously saved through Model#saveToken().
This model function is required if the refresh_token
grant is used.
Invoked during:
refresh_token
grant
Arguments:
Name |
Type |
Description |
---|---|---|
refreshToken |
String |
The access token to retrieve. |
Return value:
An Object
representing the refresh token and associated data.
token.client
and token.user
can carry additional properties that will be ignored by oauth2-server.
Remarks:
function getRefreshToken(refreshToken) {
// imaginary DB queries
return db.queryRefreshToken({refresh_token: refreshToken})
.then(function(token) {
return Promise.all([
token,
db.queryClient({id: token.client_id}),
db.queryUser({id: token.user_id})
]);
})
.spread(function(token, client, user) {
return {
refreshToken: token.refresh_token,
refreshTokenExpiresAt: token.expires_at,
scope: token.scope,
client: client, // with 'id' property
user: user
};
});
}
getClient(clientId, clientSecret)
Invoked to retrieve a client using a client id or a client id/client secret combination, depending on the grant type.
This model function is required for all grant types.
Invoked during:
authorization_code
grantclient_credentials
grantrefresh_token
grantpassword
grant
Arguments:
Name |
Type |
Description |
---|---|---|
clientId |
String |
The client id of the client to retrieve. |
clientSecret |
String |
The client secret of the client to retrieve. Can be |
Return value:
An Object
representing the client and associated data, or a falsy value if no such client could be found.
Name |
Type |
Description |
---|---|---|
client |
Object |
The return value. |
client.id |
String |
A unique string identifying the client. |
[client.redirectUris] |
Array<String> |
Redirect URIs allowed for the client. Required for the |
client.grants |
Array<String> |
Grant types allowed for the client. |
[client.accessTokenLifetime] |
Number |
Client-specific lifetime of generated access tokens in seconds. |
[client.refreshTokenLifetime] |
Number |
Client-specific lifetime of generated refresh tokens in seconds. |
The return value (client
) can carry additional properties that will be ignored by oauth2-server.
Remarks:
function getClient(clientId, clientSecret) {
// imaginary DB query
let params = {client_id: clientId};
if (clientSecret) {
params.client_secret = clientSecret;
}
return db.queryClient(params)
.then(function(client) {
return {
id: client.id,
redirectUris: client.redirect_uris,
grants: client.grants
};
});
}
getUser(username, password, client)
Invoked to retrieve a user using a username/password combination.
This model function is required if the password
grant is used.
Invoked during:
password
grant
Arguments:
Return value:
An Object
representing the user, or a falsy value if no such user could be found. The user object is completely transparent to oauth2-server and is simply used as input to other model functions.
Remarks:
function getUser(username, password) {
// imaginary DB query
return db.queryUser({username: username, password: password});
}
getUserFromClient(client)
Invoked to retrieve the user associated with the specified client.
This model function is required if the client_credentials
grant is used.
Invoked during:
client_credentials
grant
Arguments:
Name |
Type |
Description |
---|---|---|
client |
Object |
The client to retrieve the associated user for. |
client.id |
String |
A unique string identifying the client. |
Return value:
An Object
representing the user, or a falsy value if the client does not have an associated user. The user object is completely transparent to oauth2-server and is simply used as input to other model functions.
Remarks:
client
is the object previously obtained through Model#getClient().
function getUserFromClient(client) {
// imaginary DB query
return db.queryUser({id: client.user_id});
}
saveToken(token, client, user)
Invoked to save an access token and optionally a refresh token, depending on the grant type.
This model function is required for all grant types.
Invoked during:
authorization_code
grantclient_credentials
grantrefresh_token
grantpassword
grant
Arguments:
Return value:
An Object
representing the token(s) and associated data.
Name |
Type |
Description |
---|---|---|
token |
Object |
The return value. |
token.accessToken |
String |
The access token passed to |
token.accessTokenExpiresAt |
Date |
The expiry time of the access token. |
token.refreshToken |
String |
The refresh token passed to |
token.refreshTokenExpiresAt |
Date |
The expiry time of the refresh token. |
[token.scope] |
String[] |
The authorized scope of the access token. |
token.client |
Object |
The client associated with the access token. |
token.client.id |
String |
A unique string identifying the client. |
token.user |
Object |
The user associated with the access token. |
token.client
and token.user
can carry additional properties that will be ignored by oauth2-server.
If the allowExtendedTokenAttributes
server option is enabled (see OAuth2Server#token()) any additional attributes set on the result are copied to the token response sent to the client.
Remarks:
function saveToken(token, client, user) {
// imaginary DB queries
let fns = [
db.saveAccessToken({
access_token: token.accessToken,
expires_at: token.accessTokenExpiresAt,
scope: token.scope,
client_id: client.id,
user_id: user.id
}),
db.saveRefreshToken({
refresh_token: token.refreshToken,
expires_at: token.refreshTokenExpiresAt,
scope: token.scope,
client_id: client.id,
user_id: user.id
})
];
return Promise.all(fns);
.spread(function(accessToken, refreshToken) {
return {
accessToken: accessToken.access_token,
accessTokenExpiresAt: accessToken.expires_at,
refreshToken: refreshToken.refresh_token,
refreshTokenExpiresAt: refreshToken.expires_at,
scope: accessToken.scope,
client: {id: accessToken.client_id},
user: {id: accessToken.user_id}
};
});
}
revokeToken(token)
Invoked to revoke a refresh token.
This model function is required if the refresh_token
grant is used.
Invoked during:
refresh_token
grant
Arguments:
Name |
Type |
Description |
---|---|---|
token |
Object |
The token to be revoked. |
token.refreshToken |
String |
The refresh token. |
[token.refreshTokenExpiresAt] |
Date |
The expiry time of the refresh token. |
[token.scope] |
String[] |
The authorized scope of the refresh token. |
token.client |
Object |
The client associated with the refresh token. |
token.client.id |
String |
A unique string identifying the client. |
token.user |
Object |
The user associated with the refresh token. |
Return value:
Return true
if the revocation was successful or false
if the refresh token could not be found.
Remarks:
token
is the refresh token object previously obtained through Model#getRefreshToken().
function revokeToken(token) {
// imaginary DB queries
return db.deleteRefreshToken({refresh_token: token.refreshToken})
.then(function(refreshToken) {
return !!refreshToken;
});
}
validateScope(user, client, scope)
Invoked to check if the requested scope
is valid for a particular client
/user
combination.
This model function is optional. If not implemented, any scope is accepted.
Invoked during:
authorization_code
grantclient_credentials
grantpassword
grant
Arguments:
Name |
Type |
Description |
---|---|---|
user |
Object |
The associated user. |
client |
Object |
The associated client. |
client.id |
Object |
A unique string identifying the client. |
scope |
String[] |
The scopes to validate. |
Return value:
Validated scopes to be used or a falsy value to reject the requested scopes.
Remarks:
user
is the user object previously obtained through Model#getAuthorizationCode() (code.user
; authorization code grant), Model#getUserFromClient() (client credentials grant) or Model#getUser() (password grant).
client
is the object previously obtained through Model#getClient (all grants).
You can decide yourself whether you want to reject or accept partially valid scopes by simply filtering out invalid scopes and returning only the valid ones.
To reject invalid or only partially valid scopes:
// list of valid scopes
const VALID_SCOPES = ['read', 'write'];
function validateScope(user, client, scope) {
if (!scope.every(s => VALID_SCOPES.indexOf(s) >= 0)) {
return false;
}
return scope;
}
To accept partially valid scopes:
// list of valid scopes
const VALID_SCOPES = ['read', 'write'];
function validateScope(user, client, scope) {
return scope.filter(s => VALID_SCOPES.indexOf(s) >= 0);
}
verifyScope(accessToken, scope)
Invoked during request authentication to check if the provided access token was authorized the requested scopes.
This model function is required if scopes are used with OAuth2Server#authenticate()
but it’s never called, if you provide your own authenticateHandler
to the options.
Invoked during:
request authentication
Arguments:
Name |
Type |
Description |
---|---|---|
token |
Object |
The access token to test against |
token.accessToken |
String |
The access token. |
[token.accessTokenExpiresAt] |
Date |
The expiry time of the access token. |
[token.scope] |
String[] |
The authorized scope of the access token. |
token.client |
Object |
The client associated with the access token. |
token.client.id |
String |
A unique string identifying the client. |
token.user |
Object |
The user associated with the access token. |
scope |
String[] |
The required scopes. |
Return value:
Returns true
if the access token passes, false
otherwise.
Remarks:
token
is the access token object previously obtained through Model#getAccessToken().
scope
is the required scope as given to OAuth2Server#authenticate() as options.scope
.
function verifyScope(token, requestedScopes) {
if (!token.scope) {
return false;
}
let authorizedScopes = token.scope;
return requestedScopes.every(s => token.scope.includes(scope));
}
validateRedirectUri(redirectUri, client)
Invoked to check if the provided redirectUri
is valid for a particular client
.
This model function is optional. If not implemented, the redirectUri
should be included in the provided redirectUris
of the client.
Invoked during:
authorization_code
grant
Arguments:
Name |
Type |
Description |
---|---|---|
redirect_uri |
String |
The redirect URI to validate. |
client |
Object |
The associated client. |
Return value:
Returns true
if the redirectUri
is valid, false
otherwise.
Remarks:
When implementing this method you should take care of possible security risks related to redirectUri
.
.. _rfc6819: https://datatracker.ietf.org/doc/html/rfc6819
Section-5.2.3.5 is implemented by default. .. _Section-5.2.3.5: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.3.5
function validateRedirectUri(redirectUri, client) {
return client.redirectUris.includes(redirectUri);
}