DEV Community

loading...
Cover image for Strapi CMS Notes

Strapi CMS Notes

gaurangrshah profile image gshah2020 ・7 min read

https://strapi-showcase.s3-us-west-2.amazonaws.com/CheatSheet.pdf

Install with postgres:

npx create-strapi-app backend
? Choose your default database client postgres
? Database name: strapi
? Host: 127.0.0.1
? Port: 5432
? Username: strapi
? Password: ******
? Enable SSL connection: No

Authenticating

https://strapi.io/documentation/v3.x/guides/api-token.html#introduction

NOTE: strapi handles users and administrators separately. Users will have access to your client side, where as administrators have access to the strapi-based backend as well. Administrators are users, but user's are not necessarily adminstrators.

Users are basically just a collection-type in strapi, that have some default permissions and roles applied for us out of the box. Users can be managed like any other collection-type, with all basic CRUD functionality available out of the box.

Users can also be blocked from the strapi admin panel, so if you ever need to revoke a user's access this can be done from the Blocked setting on the user's profile in the admin panel

Default Auth for Administrators:

Login

POST ||  "\auth\login"                          => {token, user}
Enter fullscreen mode Exit fullscreen mode
// request.json

{
  "identifier": "you@youremail.com",
  "password": "yourpassword"
}
Enter fullscreen mode Exit fullscreen mode

image-20201102125011400

Registration

POST ||  "\auth\login\register"         => {token, user}
Enter fullscreen mode Exit fullscreen mode
// request.js

{
  "username": "yourusername",
  "email": "you@youremail.com",
  "password": "yourpassword"
}
Enter fullscreen mode Exit fullscreen mode

JWT Access Token

In order to make authenticated requests, we'll need to grab the authToken that is returned from either the login or the registration endpoints and use that in order to populate our authorization header to send alongside our requests

{
 "Authorization": "Bearer <accessToken>"
}

Authenticated routes that do no have an authorization header or ones that provide invalid tokens, will fail with a 403 forbidden error.

Access Authenticated Routes

GET || http://localhost:1337/articles
Enter fullscreen mode Exit fullscreen mode

image-20201102125540883

POST || http://localhost:1337/articles
Enter fullscreen mode Exit fullscreen mode
{
  "user": 1,
  "title": "This is the title of my article",
  "content": "This is some authenticated request content"
}
Enter fullscreen mode Exit fullscreen mode

image-20201102125920049

Current User

We can also determine the user details of the current user, by hitting a specific endpoint generated by strapi:

image-20201102124705802

GET || http://localhost:1337/users/me           => {user}
Enter fullscreen mode Exit fullscreen mode

Authentication Providers

Strapi provides several built-in authentication provider profiles, that we can simply generate an api key for. As an example we'll take a quick look at how to setup google as a strapi authentication provider.

NOTE: In order to use third party auth providers our strapi backend must be available to the web. For local development installations we can simply just use ngrok to provide us with an ssh tunnel that will expose our local environment for us.

./ngrok http 1337

This will spin up a local ngrok tunnel for our strapi application which is running on port 1337

ngrok by @inconshreveable                                                                                                                                        (Ctrl+C to quit)

Session Status                online                                                                                                                                             
Account                       Gaurang Shah (Plan: Free)                                                                                                                          
Version                       2.3.35                                                                                                                                             
Region                        United States (us)                                                                                                                                 
Web Interface                 http://127.0.0.1:4040                                                                                                                              
Forwarding                    http://6eb63f1d736e.ngrok.io -> http://localhost:1337                                                                                              
Forwarding                    https://6eb63f1d736e.ngrok.io -> http://localhost:1337                                                                                             

Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                        
                              0       0       0.00    0.00    0.00    0.00  

Now if we were to navigate to: http://6eb63f1d736e.ngrok.io we will be able to access our local strapi install while ngrok is running.

Setup Google Authentication

https://console.developers.google.com

  • [ ] Register a new project with google console

    • (click new project from project list dialog at the top)
  • [ ] Define project name (will be used to identify your project in google's console)

  • [ ] Setup OAuth Consent Screen (External = Open to Public Signup)

    • [ ] Add an application name
    • [ ] ⚠️ DO NOT add an application logo (this triggers google's approval process, we can avoid that by leaving it blank)
    • [ ] Add "Authorized Domains" (ngrok link w/ no protocol or trailing slash)
    6eb63f1d736e.ngrok.io
    
    • [ ] NOTE: Add links to homepage, privacy policy, and terms of service
    • [ ] SAVE
  • [ ] Generate OAuth Credentials

    • [ ] Click Create Credentials
    • [ ] Select OAuth Client ID
    • [ ] set Application Type : web application
    • [ ] assign a name (use the same as the project -- keep it simple)
    • [ ] Authorized Javascript Origins: (no trailing slash)
    https://6eb63f1d736e.ngrok.io
    
    • [ ] Authorized redirect URIs
    http://6eb63f1d736e.ngrok.io/connect/google/callback
    

    ⚠️ NOTE must use http with local ngrok tunnel

    callback used by google when a authentication request is triggered

    • [ ] SAVE
  • [ ] Save Credentials to Notes (Client ID, Client Secret)

Client ID : 426492773429-f91fhpg1l820clhrfoge5cb59lnh0t27.apps.googleusercontent.com

Client Secret: sbh5eD4eydVjKMMkReU7Ss8K

Configure Strapi Google Auth Credentials

  • [ ] Enable Google as a provider
  • [ ] Add Client ID
  • [ ] Add Client Secret
  • [ ] prefix the redirectURL with our ngrok tunnel address

image-20201102150230162

image-20201102150505310

Connect To Google OAuth

http://6eb63f1d736e.ngrok.io/connect/google
Enter fullscreen mode Exit fullscreen mode

image-20201102151133561

☝️ NOTE: When we navigate to the address above we get an 400 invalid_request error. This is because, Strapi uses a custom parameter called: strapi.config.server.url to determine the correct url where strapi is currently being served from.

Since we're running an ngrok tunnel strapi doesn't recognize that it is being tunneled into and so we get the invalid_request error, because the parameter never got set since we tunneled in.

To fix this we'll need to update our server settings and add the ngrok url so that strapi is aware of it:

// config/server.js

module.exports = ({env}) => ({
  /* ... */
  url: "http://6eb63f1d736e.ngrok.io",
  /* ... */
})

NOTE: be sure to leave off the trailing slash on the url

Now your changes should be reflected in your strapi console, when strapi restarts:


 Project information                                                          

┌────────────────────┬──────────────────────────────────────────────────┐
│ Time               │ Mon Nov 02 2020 15:16:26 GMT-0500 (Eastern Stan… │
│ Launched in        │ 4480 ms                                          │
│ Environment        │ development                                      │
│ Process PID        │ 22312                                            │
│ Version            │ 3.2.4 (node v14.0.0)                             │
│ Edition            │ Community                                        │
└────────────────────┴──────────────────────────────────────────────────┘

 Actions available                                                            

Welcome back!
To manage your project 🚀, go to the administration panel at:
http://6eb63f1d736e.ngrok.io/admin

To access the server ⚡️, go to:
http://6eb63f1d736e.ngrok.io

As we can see the url to our strapi installation now shows our ngrok url instead, so now we can try to connect to google's OAuth again:

image-20201102154132442

Upon successful authentication we get a response with a token and information associated with the google user account.

NOTE: if user tries to use an email that is already registered thru another provider (i.e., strapi's built-in email provider) you will see another 400 error :

{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"Auth.form.error.email.taken"}]}],"data":[{"messages":[{"id":"Auth.form.error.email.taken"}]}]}

This is because the email already matches an existing user.

Now that we've successfully setup a 3rd party OAuth provider, we can now utitlize the token and information google provides back to us in our application. In order to do that we'll need to update our callback url, that we set in strapi and point to the url of the application where we want to consume this from:

image-20201102154939641

WIth this in place you'll be able to make a GET request to the following endpoing from your application once a user has logged in to get their user details and jwt token from strapi:

GET || `${STRAPI_API_URL}/auth/google/callback?access_token=${access_token}`
Enter fullscreen mode Exit fullscreen mode
https://6eb63f1d736e.ngrok.io/auth/google/callback/?access_token=eylkjadfoi2490r8290riapojf09i.aowj23r90uiap023ir9fakm049urf.092u4t90ue09tu2jt4u9jg0u9
Enter fullscreen mode Exit fullscreen mode

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTYsImlhdCI6MTYwNDM1MDU3NywiZXhwIjoxNjA2OTQyNTc3fQ.sPODgmc89FTTf33lt4N6sNURaIivI2otQeO5Nuu2fKQ

Creating Email Service

  • [ ] Install NodeMailer as Email Provider:
  npm i strapi-provider-email-nodemailer-v3
Enter fullscreen mode Exit fullscreen mode

other email providers

strapi-docs

Configure Nodemailer as as strapi provider

// config/plugins.js

module.exports = ({ env }) => ({
  email: {
    provider: "nodemailer-v3",
    providerOptions: {},
    settings: {
      host: "process.env.SMTP_HOST",  
      port: 587,
      username: "process.env.EMAIL_ADDRESS",
      password: "process.env.EMAIL_PASSWORD",
      secure: false, // docs suggest keeping this as false when using port 587
    },
  },
})

Enter fullscreen mode Exit fullscreen mode

be sure to grab the config from your test email account, or use: https://ethereal.email

npx strapi generate:service <servicename>

npx strapi generate:service email
Enter fullscreen mode Exit fullscreen mode

This will create the following folder structure:

api
├── email
│   ├── documentation
│   └── services
Enter fullscreen mode Exit fullscreen mode

And this is the one file the command generates.

// api/email/services/email.js

'use strict';

/**
 * `email` service.
 */

module.exports = {
  // exampleService: (arg1, arg2) => {
  //   return isUserOnline(arg1, arg2);
  // }
};

Enter fullscreen mode Exit fullscreen mode

we'll update the service with our own logic

module.exports = {
  sendEmail: async(to, subject, html) => {
    // ☝️ creates logs for each email sent that show in the console
    strapi.log.info("sendEmail: Sending Email")
    console.log('hello')
    // ☝️ references the built-in email plugin to envoke it's send(fn)
    await strapi.plugins["email"].services.email.send({
      // the basic configuration required by strapi's built-in email plugin
      to,
      from: process.env.EMAIL_ADDRESS,
      replyTo: process.env.EMAIL_ADDRESS,
      subject,
      html,
    })
    strapi.log.info("sendEmail: Email Sent")
  }
};
Enter fullscreen mode Exit fullscreen mode

Set up Cron task for testing email implementation:

Next thing we'll need to do is enable strapi's cron task feature through our server, this will simply allow us to setup a cron job to test that the server is able to send emails

// config/server.js

module.exports = ({ env }) => ({
  host: env("HOST", "0.0.0.0"),
  port: env.int("PORT", 1337),

  cron: { enabled: true }, // enables all cron tasks

  admin: {
    auth: {
      secret: env("ADMIN_JWT_SECRET", "1f2afe006aac41e4ed791f66babcc874"),
    },
    host: "0.0.0.0",
    port: 3000, // --watch-admin
  },
});

Now we can setup our cron task:

  '0 * * * * *': async () => {
    return await strapi.services.email.sendEmail(
      process.env.EMAIL_ADDRESS,
      "Test Subject Line",
      "<div class"test">Test Context</div>" // include string or html
    )
  }

*❗️ NOTE: * the asterisks mean that this task will run every second. to stop the task use the commented setting. Specifying 0seconds means it will run once every minute instead. (basically when the seconds reset back to zero after 0:59).

Policies

Policies are functions which have the ability to execute specific logic on each request before it reaches the controller's action. They are mostly used for securing business logic easily. Each route of the project can be associated to an array of policies

For example, you can create a policy named isAdmin, which obviously checks that the request is sent by an admin user, and use it for critical routes.

Discussion

pic
Editor guide