Strapi CMS Notes
Tags: jamstack, cms, backend, node
Published: 2020-12-01 18:46:06



> 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}
```

```json
// request.json

{
  "identifier": "you@youremail.com",
  "password": "yourpassword"
}
```

![image-20201102125011400](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102125011400.png)

**Registration**

```
POST ||  "\auth\login\register"			=> {token, user}
```

```json
// request.js

{
  "username": "yourusername",
  "email": "you@youremail.com",
  "password": "yourpassword"
}
```



### 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
>
>```json
>{
>  "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
```

![image-20201102125540883](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102125540883.png)



```
POST || http://localhost:1337/articles
```

```json
{
  "user": 1,
  "title": "This is the title of my article",
  "content": "This is some authenticated request content"
}
```

![image-20201102125920049](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102125920049.png)



 

### Current User

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

![image-20201102124705802](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102124705802.png)

```
GET || http://localhost:1337/users/me 			=> {user}
```





## 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.

<iframe width="560" height="315" src="https://www.youtube.com/embed/vGtVSwpOlsM" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>



> **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
>
> ```shell
> 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](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102150230162.png)

![image-20201102150505310](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102150505310.png)

### Connect To Google OAuth

```shell
http://6eb63f1d736e.ngrok.io/connect/google
```

> ![image-20201102151133561](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102151133561.png)
>
> **☝️ 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:
>
> ```js
> // 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:
>
> ```shell
> 
>  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](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102154132442.png)
>
> 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 :
>
> ```json
> {"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](https://cdn.jsdelivr.net/gh/gaurangrshah/_shots@master/scrnshots/image-20201102154939641.png)

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}`
```

```
https://6eb63f1d736e.ngrok.io/auth/google/callback/?access_token=eylkjadfoi2490r8290riapojf09i.aowj23r90uiap023ir9fakm049urf.092u4t90ue09tu2jt4u9jg0u9
```

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTYsImlhdCI6MTYwNDM1MDU3NywiZXhwIjoxNjA2OTQyNTc3fQ.sPODgmc89FTTf33lt4N6sNURaIivI2otQeO5Nuu2fKQ





















## Creating Email Service

- [ ] Install NodeMailer as Email Provider:

  ```shell
  npm i strapi-provider-email-nodemailer-v3
  ```



[other email providers](https://www.npmjs.com/package/strapi-provider-email-nodemailer-v3)

[strapi-docs](https://strapi.io/documentation/3.0.0-beta.x/plugins/email.html#programmatic-usage)



Configure Nodemailer as as strapi provider

```js
// 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
    },
  },
})

```

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





```shell
npx strapi generate:service <servicename>

npx strapi generate:service email
```



This will create the following folder structure:

```shell
api
├── email
│   ├── documentation
│   └── services
```



And this is the one file the command generates.

```js
// api/email/services/email.js

'use strict';

/**
 * `email` service.
 */

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

```



we'll update the service with our own logic

```js
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")
  }
};
```





> ### 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
>
> ```js
> // 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:
>
> ```js
>   '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 <u>will run every second</u>. 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](https://strapi.io/documentation/v3.x/concepts/policies.html#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.



---

Hey everyone, I'm Gaurang, a full-stack developer that is constantly learning and building in public. I love that I was able to change my career at such a late stage in life all by learning online with so many different resources. I generate content to reinforce what I'm learning and to help others like me in the future.

If you enjoyed my content, please feel free to connect with me [wherever you spend your time online](https://links.gshahdev.com/all-links).