API tokens

In this guide we will see how you can create an API token system to execute request as an authenticated user.

This feature is in our roadmapAPI tokens - 图1 (opens new window). This guide is a workaround to achieve this feature before we support it natively in strapi.

Introduction

The goal is to be able to request API endpoints with a query parameter token that authenticates as a user. eg. /restaurants?token=my-secret-token.

To achieve this feature in development, we will have to customize the users-permissions plugin. This guide will help you understand how to customize all your applications. You can read more about Strapi plugins and customization.

Create the Token Content Type

To manage your tokens, you will have to create a new Content Type named token.

  • string attribute named token
  • relation attribute Token (user) - Token has and belongs to one User - User (token)

Then add some users and create some token linked to these users.

Setup the file to override

We now have to customize the function that verifies the token token. Strapi has an Authentication process that uses JWT tokens, we will reuse this function to customize the verification.

Here is the functionAPI tokens - 图2 (opens new window) that manages the JWT validation.

To be able to customize it, you will have to create a new file in your application ./extensions/users-permissions/config/policies/permissions.js.

Then copy the original function that is on GitHub and paste it in your new file.

When it’s done, the Strapi application will use this function instead of the core one. We are ready to customize it.

Add token validation logic

You will have to update the first lines of this function.

Path — ./extensions/users-permissions/config/policies/permissions.js

  1. const _ = require('lodash');
  2. module.exports = async (ctx, next) => {
  3. let role;
  4. if (ctx.state.user) {
  5. // request is already authenticated in a different way
  6. return next();
  7. }
  8. // add the detection of `token` query parameter
  9. if (
  10. (ctx.request && ctx.request.header && ctx.request.header.authorization) ||
  11. (ctx.request.query && ctx.request.query.token)
  12. ) {
  13. try {
  14. // init `id` and `isAdmin` outside of validation blocks
  15. let id;
  16. let isAdmin;
  17. if (ctx.request.query && ctx.request.query.token) {
  18. // find the token entry that match the token from the request
  19. const [token] = await strapi.query('token').find({token: ctx.request.query.token});
  20. if (!token) {
  21. throw new Error(`Invalid token: This token doesn't exist`);
  22. } else {
  23. if (token.user && typeof token.token === 'string') {
  24. id = token.user.id;
  25. }
  26. isAdmin = false;
  27. }
  28. delete ctx.request.query.token;
  29. } else if (ctx.request && ctx.request.header && ctx.request.header.authorization) {
  30. // use the current system with JWT in the header
  31. const decrypted = await strapi.plugins[
  32. 'users-permissions'
  33. ].services.jwt.getToken(ctx);
  34. id = decrypted.id;
  35. isAdmin = decrypted.isAdmin || false;
  36. }
  37. // this is the line that already exist in the code
  38. if (id === undefined) {
  39. throw new Error('Invalid token: Token did not contain required fields');
  40. }
  41. ...

And tada! You can now create a token, link it to a user and use it in your URLs with token as query parameters.