Lab - Simple OIDC Provider
Context Help understanding how an ODIC provider works. Be able to set up and experiment with an OIDC client to interact with .
Preparation
Create a folder and initialize a Node.js project
$ mkdir oidcsrv
$ cd oidcsrv
$ yarn init -y
$ yarn add express oidc-provider
Change the project to be ESM by adding or updating the line in package.json
:
{
...
"type": "module",
...
}
Add the Code
Add index.js
in the top folder:
import express from 'express';
import Provider from 'oidc-provider';
const app = express();
const configuration = {
clients: [{
client_id: 'some_client_id',
client_secret: 'some_client_secret',
grant_types: ['authorization_code'],
redirect_uris: ['http://localhost:8080/login/callback'],
post_logout_redirect_uris: ['http://localhost:8080/'],
response_types: ['code'],
}],
pkce: {
required: () => true,
},
async findAccount(ctx, id) {
return {
accountId: id,
async claims(use, scope) {
return { sub: id };
},
};
},
};
const oidc = new Provider('http://localhost:3000', configuration);
app.use('/oidc', oidc.callback());
app.listen(3000, function () {
console.log('OIDC is listening on http://localhost:3000');
});
Launch the Server
Launch the server with node index.js
:
$ node index.js
# Warning skipped
OIDC is listening on http://localhost:3000
Notes
- The server is set up with one client of specific configuration
- PKCE is made required, which is also true by default in most cases
- Any user is accepted, with template reply
- The provider is at the location
/oidc
, so the client need to setissuer
correctly
email
and profile
Scopes and Claims
In order to serve additional scopes other than openid
, such as the common email
and profile
scopes, the code index.js
can be changed to:
import express from 'express';
import Provider from 'oidc-provider';
const app = express();
const configuration = {
clients: [{
client_id: 'oidccli_id',
client_secret: 'oidccli_secret',
grant_types: ['authorization_code'],
redirect_uris: ['http://localhost:8080/login/callback'],
post_logout_redirect_uris: ['http://localhost:8080/'],
response_types: ['code'],
}],
scopes: [
'email', 'profile',
],
claims: {
email: ['email'],
profile: ['first_name'],
},
async findAccount(ctx, id) {
return {
accountId: id,
async claims(use, scope) {
console.log("USE", use, "SCOPE", scope);
return { sub: id, email: `${id}@example.com`, first_name: id };
},
};
},
};
const oidc = new Provider('http://localhost:3000', configuration);
app.use('/oidc', oidc.callback());
app.listen(3000, function () {
console.log('OIDC is listening on http://localhost:3000');
});
- Here the
findAccount()
function needs to be revised, of course. - The
scopes
andclaims
settings configure what scopes, and which attributes for each scope, to provide