Node.js Adapter
{project_name} provides a Node.js adapter built on top of Connect to protect server-side JavaScript apps - the goal was to be flexible enough to integrate with frameworks like Express.js.
To use the Node.js adapter, first you must create a client for your application in the {project_name} Administration Console. The adapter supports public, confidential, and bearer-only access type. Which one to choose depends on the use-case scenario.
Once the client is created click the Installation
tab, select {project_name} OIDC JSON
for Format Option
, and then click Download
. The downloaded keycloak.json
file should be at the root folder of your project.
Installation
Assuming you’ve already installed Node.js, create a folder for your application:
mkdir myapp && cd myapp
Use npm init
command to create a package.json
for your application. Now add the {project_name} connect adapter in the dependencies list:
Usage
- Instantiate a Keycloak class
-
The
Keycloak
class provides a central point for configuration and integration with your application. The simplest creation involves no arguments.
var session = require('express-session');
var Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
By default, this will locate a file named keycloak.json
alongside
the main executable of your application to initialize keycloak-specific
settings (public key, realm name, various URLs). The keycloak.json
file
is obtained from the {project_name} Admin Console.
Instantiation with this method results in all of the reasonable defaults
being used. As alternative, it’s also possible to provide a configuration
object, rather than the keycloak.json
file:
let kcConfig = {
clientId: 'myclient',
bearerOnly: true,
serverUrl: 'http://localhost:8080/auth',
realm: 'myrealm',
realmPublicKey: 'MIIBIjANB...'
};
let keycloak = new Keycloak({ store: memoryStore }, kcConfig);
Applications can also redirect users to their preferred identity provider by using:
let keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
- Configuring a web session store
-
If you want to use web sessions to manage server-side state for authentication, you need to initialize the
Keycloak(…)
with at least astore
parameter, passing in the actual session store thatexpress-session
is using.
var session = require('express-session');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
- Passing a custom scope value
-
By default, the scope value
openid
is passed as a query parameter to {project_name}'s login URL, but you can add an additional custom value:
var keycloak = new Keycloak({ scope: 'offline_access' });
Installing Middleware
Once instantiated, install the middleware into your connect-capable app:
var app = express();
app.use( keycloak.middleware() );
Configuration for Proxies
If the application is running behind a proxy that terminates an SSL connection Express must be configured per the express behind proxies guide. Using an incorrect proxy configuration can result in invalid redirect URIs being generated.
Example configuration:
var app = express();
app.set( 'trust proxy', true );
app.use( keycloak.middleware() );
Checking Authentication
To check that a user is authenticated before accessing a resource,
simply use keycloak.checkSso()
. It will only authenticate if the user is already logged-in. If the user is not logged-in, the browser will be redirected back to the originally-requested URL and remain unauthenticated:
app.get( '/check-sso', keycloak.checkSso(), checkSsoHandler );
Protecting Resources
- Simple authentication
-
To enforce that a user must be authenticated before accessing a resource, simply use a no-argument version of
keycloak.protect()
:
app.get( '/complain', keycloak.protect(), complaintHandler );
- Role-based authorization
-
To secure a resource with an application role for the current app:
app.get( '/special', keycloak.protect('special'), specialHandler );
To secure a resource with an application role for a different app:
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
To secure a resource with a realm role:
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
- Resource-Based Authorization
-
Resource-Based Authorization allows you to protect resources, and their specific methods/actions,** based on a set of policies defined in Keycloak, thus externalizing authorization from your application. This is achieved by exposing a
keycloak.enforcer
method which you can use to protect resources.*
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
The keycloak-enforcer
method operates in two modes, depending on the value of the response_mode
configuration option.
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
If response_mode
is set to token
, permissions are obtained from the server on behalf of the subject represented by the bearer token that was sent to your application. In this case, a new access token is issued by Keycloak with the permissions granted by the server. If the server did not respond with a token with the expected permissions, the request is denied. When using this mode, you should be able to obtain the token from the request as follows:
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) {
var token = req.kauth.grant.access_token.content;
var permissions = token.authorization ? token.authorization.permissions : undefined;
// show user profile
});
Prefer this mode when your application is using sessions and you want to cache previous decisions from the server, as well automatically handle refresh tokens. This mode is especially useful for applications acting as a client and resource server.
If response_mode
is set to permissions
(default mode), the server only returns the list of granted permissions, without issuing a new access token. In addition to not issuing a new token, this method exposes the permissions granted by the server through the request
as follows:
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
var permissions = req.permissions;
// show user profile
});
Regardless of the response_mode
in use, the keycloak.enforcer
method will first try to check the permissions within the bearer token that was sent to your application. If the bearer token already carries the expected permissions, there is no need
to interact with the server to obtain a decision. This is specially useful when your clients are capable of obtaining access tokens from the server with the expected permissions before accessing a protected resource, so they can use some capabilities provided by Keycloak Authorization Services such as incremental authorization and avoid additional requests to the server when keycloak.enforcer
is enforcing access to the resource.
By default, the policy enforcer will use the client_id
defined to the application (for instance, via keycloak.json
) to
reference a client in Keycloak that supports Keycloak Authorization Services. In this case, the client can not be public given
that it is actually a resource server.
If your application is acting as both a public client(frontend) and resource server(backend), you can use the following configuration to reference a different client in Keycloak with the policies that you want to enforce:
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
It is recommended to use distinct clients in Keycloak to represent your frontend and backend.
If the application you are protecting is enabled with Keycloak authorization services and you have defined client credentials
in keycloak.json
, you can push additional claims to the server and make them available to your policies in order to make decisions.
For that, you can define a claims
configuration option which expects a function
that returns a JSON with the claims you want to push:
app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], {
claims: function(request) {
return {
"http.uri": ["/protected/resource"],
"user.agent": // get user agent from request
}
}
}), function (req, res) {
// access granted
For more details about how to configure Keycloak to protected your application resources, please take a look at the {authorizationguide_name}.
- Advanced authorization
-
To secure resources based on parts of the URL itself, assuming a role exists for each section:
function protectBySection(token, request) {
return token.hasRole( request.params.section );
}
app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
Additional URLs
- Explicit user-triggered logout
-
By default, the middleware catches calls to
/logout
to send the user through a {project_name}-centric logout workflow. This can be changed by specifying alogout
configuration parameter to themiddleware()
call:
app.use( keycloak.middleware( { logout: '/logoff' } ));
When the user-triggered logout is invoked a query parameter redirect_url
can be passed:
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
This parameter is then used as the redirect url of the OIDC logout endpoint and the user will be redirected to
https://example.com/logged/out
.
- {project_name} Admin Callbacks
-
Also, the middleware supports callbacks from the {project_name} console to log out a single session or all sessions. By default, these type of admin callbacks occur relative to the root URL of
/
but can be changed by providing anadmin
parameter to themiddleware()
call:
app.use( keycloak.middleware( { admin: '/callbacks' } );