Express middleware for dealing with common tasks related to authentication and authorization with the Zetkin Platform. It contains middleware functions and endpoint handlers for:
- Storing and retrieving Zetkin API token from cookies
- Validating token ahead of endpoints that require it
- Redirecting to, and handling redirects back from Zetkin Platform login flow
- Logging out, destroying session and clearing cookie
Note that this middleware is closely related to, but not the same as, the
Zetkin javascript SDK. This piece of
documentation repeatedly refers to the Zetkin SDK or it's main Z
class.
The below example contains all the code necessary to set up authentication with the Zetkin API.
import auth from 'express-zetkin-auth';
const authOpts = {
app: {
id: "myAppId",
secret: "mySecretAppKey"
}
};
app.use(auth.initialize(authOpts));
app.get('/logout', auth.logout());
// Uses validate() to make sure user is logged in
app.get('/my/page', auth.validate(authOpts), (req, res) => {
// Because initialize() ran earlier, req.z exists
req.z.resource('users', 'me')
.get()
.then(res => {
res.send('Hello ' + res.data.email);
});
});
An anonymous user that tries to access /my/page will be redirected to the Zetkin Platform login flow where they will be asked to log in and authorize access to the Zetkin API to our application. The user will then be redirected back to /my/page where they will now be able to access the data retrieved using the Zetkin SDK.
All functions accept a single attribute which should be an object with the following format.
const authOpts = {
cookieName: "apiToken", // Optional
defaultRedirPath: "", // Optional
logoutRedirPath: "", // Optional
zetkinDomain: "zetk.in", // Optional
app: { // Required
id: "myAppId",
secret: "myAppSecret"
}
};
The only required option, for which there are no default values, is the app
object containing application credentials.
The application credentials are defined as an object with attributes id
and
key
for the application ID and secret key respectively. If you do not have
any application credentials, first register your application with the Zetkin
Platform.
The key should be kept secret. Avoid storing it in code that could be accessed
by others, e.g. on GitHub. Instead, store your app key elsewhere and inject it
using an environment variable (via e.g. process.env.ZETKIN_APP_KEY
) or some
other means.
This is the name used to store the serialized user ticket in a browser cookie, which can then be accessed on the client to make requests directly.
This is the domain on which Zetkin is running, by default this is the normal Zetkin instance at zetk.in (api.zetk.in, login.zetk.in etc).
This attribute should only be used explicitly in Zetkin development when the log in portal is running on the Zetkin development server or locally. The default value zetk.in is correct for any other case.
These values define where a user should be redirected after a successful logout
or login operation (unless the login flow was initiated automatically by
validate()
in which case the user will be redirected back to the originally
requested path).
If no logoutRedirPath
is defined, users will be redirected to the
defaultRedirPath
after logging out.
This function creates a Zetkin SDK Z
instance on the express req
object and
looks for a Zetkin API token in the cookies, using the name in the cookieName
attribute on the options object. It also handles redirects back from the login
flow.
app.use(initialize(authOpts));
Adding the initialize()
middleware like this makes an instance of the Zetkin
SDK Z
class, configured with any ticket found in the cookies, available to all
subsequent handlers as req.z
.
This function creates a middleware suitable for validating user authentication. It does not deal with authorization at all, instead only validating that a ticket exists and that it is valid in the sense that it has not expired and represents a user signed into the Zetkin Platform.
If no ticket exists, or the ticket is not valid for access to the Zetkin API,
this middleware redirects the user to the login page as defined by the
loginUrl
option, sending along the application ID as defined by app.id
as
well as the requested path from req.path
.
let opts = {
app: {
id: 'myAppId'
}
};
app.all([ '/a/protected/path', '/another/protected/path' ], validate(opts));
The user will be required to log in before returning to the application, where
the callback()
endpoint can be used to finish the login flow.
This function creates an endpoint that will redirect the user to the correct
login URL, and then back to whatever is submitted as redirPath
in the query
string. This lets you easily create login links.
app.get('/login', login(opts));
A login link could then point to /login?redirPath=/restricted.
This function creates an endpoint that instantly logs the current user out and
redirects them to the path given by logoutRedirPath
, or defaultRedirPath
as
a fallback.
app.all('/logout', logout());