gshah2020
Posted on December 1, 2020
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
andadministrators
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}
// request.json
{
"identifier": "you@youremail.com",
"password": "yourpassword"
}
Registration
POST || "\auth\login\register" => {token, user}
// 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
{ "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
POST || http://localhost:1337/articles
{
"user": 1,
"title": "This is the title of my article",
"content": "This is some authenticated request content"
}
Current User
We can also determine the user details of the current user, by hitting a specific endpoint generated by strapi:
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.
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 tunnelcallback 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
Connect To Google OAuth
http://6eb63f1d736e.ngrok.io/connect/google
☝️ 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:
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:
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:
npm i strapi-provider-email-nodemailer-v3
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
},
},
})
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
This will create the following folder structure:
api
├── email
│ ├── documentation
│ └── services
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);
// }
};
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")
}
};
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.
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.
Posted on December 1, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.