From b29f4a411e511602bc2d8a1ddf137b3afd3df0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A4ser?= Date: Thu, 19 Jan 2023 18:16:03 +0100 Subject: [PATCH] feat: Add authentication Features added : - Add sign-in page - Add sign-up page - Custom endpoint in Strapi to verify email - Add JWT authentication --- apps/backend/config/plugins.ts | 22 + apps/backend/package-lock.json | 22 + apps/backend/package.json | 2 + .../api/page/content-types/page/schema.json | 4 + .../src/api/username/controllers/username.ts | 17 + .../src/api/username/routes/username.ts | 13 + .../src/api/username/services/username.ts | 18 + .../content-types/user/schema.json | 79 ++ apps/client/.env | 5 +- apps/client/.eslintrc.json | 30 +- apps/client/components/footer/footer.tsx | 2 +- .../header-auth/header-auth.module.scss | 7 + .../header-auth/header-auth.spec.tsx | 10 + .../components/header-auth/header-auth.tsx | 28 + apps/client/components/header/header.tsx | 228 +++-- .../components/layout/layout.module.scss | 62 +- apps/client/components/layout/layout.tsx | 28 +- apps/client/config.ts | 1 + apps/client/environments/environments.prod.ts | 7 + apps/client/environments/environments.ts | 7 + apps/client/libs/api.ts | 58 +- apps/client/libs/auth.ts | 26 + apps/client/pages/_app.tsx | 18 +- apps/client/pages/api/auth/[...nextauth].ts | 62 ++ apps/client/pages/home/index.module.scss | 7 + apps/client/pages/home/index.tsx | 33 + apps/client/pages/index.tsx | 6 +- apps/client/pages/sign-in/index.module.scss | 18 + apps/client/pages/sign-in/index.tsx | 121 +++ apps/client/pages/sign-up/index.module.scss | 16 + apps/client/pages/sign-up/index.tsx | 248 +++++ apps/client/pages/styles.css | 2 +- apps/client/project.json | 6 + apps/client/tailwind.config.js | 9 +- package-lock.json | 969 ++++++++---------- package.json | 8 +- 36 files changed, 1513 insertions(+), 686 deletions(-) create mode 100644 apps/backend/config/plugins.ts create mode 100644 apps/backend/src/api/username/controllers/username.ts create mode 100644 apps/backend/src/api/username/routes/username.ts create mode 100644 apps/backend/src/api/username/services/username.ts create mode 100644 apps/backend/src/extensions/users-permissions/content-types/user/schema.json create mode 100644 apps/client/components/header-auth/header-auth.module.scss create mode 100644 apps/client/components/header-auth/header-auth.spec.tsx create mode 100644 apps/client/components/header-auth/header-auth.tsx create mode 100644 apps/client/environments/environments.prod.ts create mode 100644 apps/client/environments/environments.ts create mode 100644 apps/client/libs/auth.ts create mode 100644 apps/client/pages/api/auth/[...nextauth].ts create mode 100644 apps/client/pages/home/index.module.scss create mode 100644 apps/client/pages/home/index.tsx create mode 100644 apps/client/pages/sign-in/index.module.scss create mode 100644 apps/client/pages/sign-in/index.tsx create mode 100644 apps/client/pages/sign-up/index.module.scss create mode 100644 apps/client/pages/sign-up/index.tsx diff --git a/apps/backend/config/plugins.ts b/apps/backend/config/plugins.ts new file mode 100644 index 0000000..b269e93 --- /dev/null +++ b/apps/backend/config/plugins.ts @@ -0,0 +1,22 @@ +module.exports = ({env}) => ({ + // ... + email: { + config: { + provider: 'nodemailer', + providerOptions: { + host: env('SMTP_HOST', 'smtp.example.com'), + port: env('SMTP_PORT', 587), + auth: { + user: env('SMTP_USERNAME'), + pass: env('SMTP_PASSWORD'), + }, + // ... any custom nodemailer options + }, + settings: { + defaultFrom: 'no-reply@naser.fr', + defaultReplyTo: 'contact@naser.fr', + }, + }, + }, + // ... +}); diff --git a/apps/backend/package-lock.json b/apps/backend/package-lock.json index c7ed4ef..79f8598 100644 --- a/apps/backend/package-lock.json +++ b/apps/backend/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@strapi/plugin-i18n": "4.5.4", "@strapi/plugin-users-permissions": "4.5.4", + "@strapi/provider-email-nodemailer": "^4.5.6", "@strapi/strapi": "4.5.4", "@strapi/utils": "^4.5.6", "better-sqlite3": "7.4.6", @@ -4084,6 +4085,19 @@ "node": ">=10" } }, + "node_modules/@strapi/provider-email-nodemailer": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@strapi/provider-email-nodemailer/-/provider-email-nodemailer-4.5.6.tgz", + "integrity": "sha512-Tpv0WL4BTYyXXY5eMt6pwnMdwVb3RrDPjRH9BA7LHYAfAQCk4wy/7uWoHJYPoQG3qvwkNihWkMjdcb3cL52n6A==", + "dependencies": { + "lodash": "4.17.21", + "nodemailer": "6.8.0" + }, + "engines": { + "node": ">=14.19.1 <=18.x.x", + "npm": ">=6.0.0" + } + }, "node_modules/@strapi/provider-email-sendmail": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/@strapi/provider-email-sendmail/-/provider-email-sendmail-4.5.4.tgz", @@ -11896,6 +11910,14 @@ "node": ">=6" } }, + "node_modules/nodemailer": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", + "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemailer-fetch": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", diff --git a/apps/backend/package.json b/apps/backend/package.json index 748e862..ef0b305 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -4,6 +4,7 @@ "version": "0.1.0", "description": "A Strapi application", "scripts": { + "generate": "strapi generate", "develop": "strapi develop", "start": "strapi start", "build": "strapi build", @@ -15,6 +16,7 @@ "dependencies": { "@strapi/plugin-i18n": "4.5.4", "@strapi/plugin-users-permissions": "4.5.4", + "@strapi/provider-email-nodemailer": "^4.5.6", "@strapi/strapi": "4.5.4", "@strapi/utils": "^4.5.6", "better-sqlite3": "7.4.6", diff --git a/apps/backend/src/api/page/content-types/page/schema.json b/apps/backend/src/api/page/content-types/page/schema.json index 5100f7e..04076af 100644 --- a/apps/backend/src/api/page/content-types/page/schema.json +++ b/apps/backend/src/api/page/content-types/page/schema.json @@ -23,6 +23,10 @@ "type": "component", "repeatable": false, "component": "shared.seo" + }, + "Login": { + "type": "dynamiczone", + "components": [] } } } diff --git a/apps/backend/src/api/username/controllers/username.ts b/apps/backend/src/api/username/controllers/username.ts new file mode 100644 index 0000000..645f7df --- /dev/null +++ b/apps/backend/src/api/username/controllers/username.ts @@ -0,0 +1,17 @@ +/** + * A set of functions called "actions" for `username` + */ + +export default { + findOne: async (ctx, next) => { + try { + const data = await strapi + .service("api::username.username") + .findUsername(ctx.params.username); + + ctx.body = data; + } catch (err) { + ctx.badRequest("Post report controller error", {moreDetails: err}); + } + } +}; diff --git a/apps/backend/src/api/username/routes/username.ts b/apps/backend/src/api/username/routes/username.ts new file mode 100644 index 0000000..3f978ba --- /dev/null +++ b/apps/backend/src/api/username/routes/username.ts @@ -0,0 +1,13 @@ +export default { + routes: [ + { + method: 'GET', + path: '/username/:username', + handler: 'username.findOne', + config: { + policies: [], + middlewares: [], + }, + }, + ], +}; diff --git a/apps/backend/src/api/username/services/username.ts b/apps/backend/src/api/username/services/username.ts new file mode 100644 index 0000000..684bda5 --- /dev/null +++ b/apps/backend/src/api/username/services/username.ts @@ -0,0 +1,18 @@ +/** + * username service + */ + +export default () => ({ + findUsername: async (username) => { + try { + console.log(username); + let users = await strapi.entityService.findMany( + "plugin::users-permissions.user", {filters: {email: username}} + ); + + return users.length !== 0; + } catch (err) { + return err; + } + } +}); diff --git a/apps/backend/src/extensions/users-permissions/content-types/user/schema.json b/apps/backend/src/extensions/users-permissions/content-types/user/schema.json new file mode 100644 index 0000000..6e52bd6 --- /dev/null +++ b/apps/backend/src/extensions/users-permissions/content-types/user/schema.json @@ -0,0 +1,79 @@ +{ + "kind": "collectionType", + "collectionName": "up_users", + "info": { + "name": "user", + "description": "", + "singularName": "user", + "pluralName": "users", + "displayName": "User" + }, + "options": { + "draftAndPublish": false, + "timestamps": true + }, + "attributes": { + "username": { + "type": "string", + "minLength": 3, + "unique": true, + "configurable": false, + "required": true + }, + "email": { + "type": "email", + "minLength": 6, + "configurable": false, + "required": true + }, + "provider": { + "type": "string", + "configurable": false + }, + "password": { + "type": "password", + "minLength": 6, + "configurable": false, + "private": true + }, + "resetPasswordToken": { + "type": "string", + "configurable": false, + "private": true + }, + "confirmationToken": { + "type": "string", + "configurable": false, + "private": true + }, + "confirmed": { + "type": "boolean", + "default": false, + "configurable": false + }, + "blocked": { + "type": "boolean", + "default": false, + "configurable": false + }, + "role": { + "type": "relation", + "relation": "manyToOne", + "target": "plugin::users-permissions.role", + "inversedBy": "users", + "configurable": false + }, + "avatar": { + "allowedTypes": [ + "images" + ], + "type": "media", + "configurable": false, + "multiple": false + }, + "newsletter": { + "type": "boolean", + "default": false + } + } +} diff --git a/apps/client/.env b/apps/client/.env index 172e20c..d2ea0ef 100644 --- a/apps/client/.env +++ b/apps/client/.env @@ -1 +1,4 @@ -STRAPI_URL=http://127.0.0.1:1337/api +STRAPI_URL_API=http://127.0.0.1:1337/api +STRAPI_URL=http://127.0.0.1:1337/ +NEXTAUTH_SECRET= +NEXTAUTH_URL=http://localhost:4200 diff --git a/apps/client/.eslintrc.json b/apps/client/.eslintrc.json index e4c5b6c..445a04e 100644 --- a/apps/client/.eslintrc.json +++ b/apps/client/.eslintrc.json @@ -5,25 +5,43 @@ "next/core-web-vitals", "../../.eslintrc.json" ], - "ignorePatterns": ["!**/*", ".next/**/*"], + "ignorePatterns": [ + "!**/*", + ".next/**/*" + ], "overrides": [ { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], "rules": { - "@next/next/no-html-link-for-pages": ["error", "apps/client/pages"] + "@next/next/no-html-link-for-pages": [ + "error", + "apps/client/pages" + ] } }, { - "files": ["*.ts", "*.tsx"], + "files": [ + "*.ts", + "*.tsx" + ], "rules": {} }, { - "files": ["*.js", "*.jsx"], + "files": [ + "*.js", + "*.jsx" + ], "rules": {} } ], "rules": { - "@next/next/no-html-link-for-pages": "off" + "@next/next/no-html-link-for-pages": "off", + "@typescript-eslint/ban-ts-comment": "off" }, "env": { "jest": true diff --git a/apps/client/components/footer/footer.tsx b/apps/client/components/footer/footer.tsx index 8820f3c..b76978b 100644 --- a/apps/client/components/footer/footer.tsx +++ b/apps/client/components/footer/footer.tsx @@ -17,7 +17,7 @@ export function Footer({items = []}) { return (