{"resource":{"author":{"id":"h2so9H8jPMmgUKGghoNl","name":"Yomesh Gupta","username":"yomeshgupta"},"content":{"link":null,"difficulty":1,"domain":1,"type":1,"isInternal":true,"body":"Social Login is a form of single sign-on using existing information from a social network provider like Google, Facebook, Twitter, Github, and so on. \r\n\r\nIf your app server is Express then you should check out this [blog-post](https://devtools.tech/blog/add-social-authentication-functionality-to-a-remix-run-project---rid---jVwo6agJvN2oX4IegYgz) where we added Social Auth when our server is Express. In this blog post, we would talk about how we can integrate `Login via Google` with Remix Run project when our server is Remix Server!\r\n\r\n## Setting up an app in Google Developer Console and getting GOOGLE_CLIENT_ID and GOOLGE_CLIENT_SECRET\r\n\r\nBefore we start any development, first we need to set up an app in Google Developer Console and get the necessary credentials.\r\n\r\n1. Go to [https://console.cloud.google.com/](https://console.cloud.google.com/)\r\n2. Select a project or create a new one\r\n3. Once the project is selected, go to [https://console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials)\r\n4. Open the Credentials tab from the sidebar\r\n5. Click on `Create Credentials` and select `OAuth client ID`\r\n6. Select Application type as `Web application`.\r\n7. Name your application. For e.g. `LoginApp`\r\n8. In the `Authorised redirect URIs` section, click on `ADD URI` and add `http://localhost:3000/auth/google/callback`\r\n9. Click on `CREATE`\r\n10. You will now get your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Copy and save them securely.\r\n\r\n## Create a Remix Run Project\r\n\r\nWe are going to create a fresh Remix project and choose Remix Server.\r\n\r\n```js\r\nnpx create-remix@latest\r\n# choose Remix Server\r\ncd [whatever you named the project]\r\n```\r\n\r\n## Load Secrets as ENVIRONMENT VARIABLES\r\n\r\n1. Let us first create a `.env` file to store the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` downloaded earlier.\r\n\r\n```\r\ntouch .env\r\nvi .env\r\n```\r\n\r\n```\r\nfile: .env\r\n\r\nNODE_ENV=development\r\nGOOGLE_CLIENT_ID=XXXXXX // use your GOOGLE_CLIENT_ID\r\nGOOGLE_CLIENT_SECRET=XXXXXX // use your GOOGLE_CLIENT_SECRET\r\n```\r\n\r\n2. Install `dotenv` package\r\n\r\n```\r\nnpm i dotenv\r\n```\r\n\r\n3. Update NPM scripts\r\n\r\n```js\r\n\"scripts\": {\r\n  \"dev\": \"node -r dotenv/config ./node_modules/.bin/remix dev\",\r\n  \"start\": \"node -r dotenv/config ./node_modules/.bin/remix-serve build\"\r\n},\r\n```\r\n\r\n## Creating Session Storage\r\n\r\nWe need a session storage object to store the user session. We are going to use the in-built session management utilities provided by Remix.\r\n\r\n```\r\ncd app\r\nmkdir services\r\ntouch session.server.js\r\nvi session.server.js\r\n```\r\n\r\n```js\r\n// file: app/services/session.server.js\r\n\r\nimport { createCookieSessionStorage } from \"remix\";\r\n\r\nexport const sessionStorage = createCookieSessionStorage({\r\n  cookie: {\r\n    name: \"_session\", // use any name you want here\r\n    sameSite: \"lax\", // this helps with CSRF\r\n    path: \"/\", // remember to add this so the cookie will work in all routes\r\n    httpOnly: true, // for security reasons, make this cookie http only\r\n    secrets: [\"s3cr3t\"], // replace this with an actual secret\r\n    secure: process.env.NODE_ENV === \"production\", // enable this in prod only\r\n  },\r\n});\r\n\r\nexport const { getSession, commitSession, destroySession } = sessionStorage;\r\n```\r\n\r\n## Install Dependencies\r\n\r\nFor our use case, we would need two packages:\r\n\r\n- [remix-auth](https://www.npmjs.com/package/@lgastler/remix-auth): It is a complete open-source authentication solution for Remix.run applications. This package is heavily inspired by [Passport.js](https://www.npmjs.com/package/passport) so APIs are quite similar.\r\n- [remix-auth-socials](https://www.npmjs.com/package/remix-auth-socials): This package provides different strategies like `Google`, `Facebook`, `Github` and so on. We can use these social strategies to provide the authentication flow in our app.\r\n\r\n```\r\nnpm i remix-auth remix-auth-socials\r\n```\r\n\r\n## Create Authentication Service\r\n\r\nLet us create a file called `auth.server.js`. In this file, we are going to initialize our `authenticator` and configure different social strategies.\r\n\r\n```\r\ncd app/services\r\ntouch auth.server.js\r\nvi auth.server.js\r\n```\r\n\r\n```js\r\n// file: app/services/auth.server.js\r\n\r\nimport { Authenticator } from \"remix-auth\";\r\nimport { sessionStorage } from \"~/services/session.server\";\r\n\r\n// Create an instance of the authenticator\r\n// It will take session storage as an input parameter and creates the user session on successful authentication\r\nexport const authenticator = new Authenticator(sessionStorage);\r\n```\r\n\r\n### Configure Google OAuth Strategy\r\n\r\n```js\r\n// file: app/services/auth.server.js\r\nimport { GoogleStrategy, SocialsProvider } from \"remix-auth-socials\";\r\n...\r\n\r\n// callback function that will be invoked upon successful authentication from social provider\r\nasync function handleSocialAuthCallback({ profile }) {\r\n  // create user in your db here\r\n  // profile object contains all the user data like image, displayName, id\r\n  return profile;\r\n}\r\n\r\n// Configuring Google Strategy\r\nauthenticator.use(new GoogleStrategy({\r\n    clientID: process.env.GOOGLE_CLIENT_ID,\r\n    clientSecret: process.env.GOOGLE_CLIENT_SECRET,\r\n    scope: [\"openid email profile\"],\r\n    callbackURL: `http://localhost:3000/auth/${SocialsProvider.GOOGLE}/callback`;\r\n  },\r\n  handleSocialAuthCallback\r\n));\r\n```\r\n\r\n### Create Routes that will handle our social login\r\n\r\n1. We are going to create a new route `auth.google.js`. Whenever the user clicks on the `Login with Google` button then a request would be sent to this route and it would initiate the entire authentication flow.\r\n\r\n```\r\ncd app/routes\r\ntouch auth.google.js\r\n```\r\n\r\n```js\r\n// file: app/routes/auth.google.js\r\n\r\nimport { authenticator } from \"../services/auth.server.js\";\r\nimport { SocialsProvider } from \"remix-auth-socials\";\r\n\r\nexport const action = async ({ request }) => {\r\n  // initiating authentication using Google Strategy\r\n  // on success --> redirect to dasboard\r\n  // on failure --> back to homepage/login\r\n  return await authenticator.authenticate(SocialsProvider.GOOGLE, request, {\r\n    successRedirect: \"/dashboard\",\r\n    failureRedirect: \"/\",\r\n  });\r\n};\r\n```\r\n\r\nIn the above code snippet, we export an `action` that would initiate our `Google` strategy authentication flow. If the user is successfully authenticated then it will redirect the user to `/dashboard` else back to the `/` homepage route.\r\n\r\n2. We are going to create a new route `auth.google.callback.js`. This route is the `Authorised redirect URIs` that we provided initially during our app setup. Whenever users are successfully authenticated then Google will send them to this route.\r\n\r\n```\r\ncd app/routes\r\ntouch auth.google.callback.js\r\n```\r\n\r\n```js\r\n// file: app/routes/auth.google.callback.js\r\n\r\nimport { authenticator } from \"../services/auth.server\";\r\nimport { SocialsProvider } from \"remix-auth-socials\";\r\n\r\nexport const loader = ({ request }) => {\r\n  return authenticator.authenticate(SocialsProvider.GOOGLE, request, {\r\n    successRedirect: \"/dashboard\",\r\n    failureRedirect: \"/\",\r\n  });\r\n};\r\n```\r\n\r\n## Create Routes to test the flow\r\n\r\n- Let us add a `Login with Google` button to our homepage.\r\n\r\n```js\r\n// file: app/routes/index.js\r\n\r\nimport { Form } from \"remix\";\r\nimport { SocialsProvider } from \"remix-auth-socials\";\r\n\r\nconst CONTAINER_STYLES = {\r\n  width: \"100%\",\r\n  height: \"100vh\",\r\n  display: \"flex\",\r\n  justifyContent: \"center\",\r\n  alignItems: \"center\",\r\n};\r\n\r\nconst BUTTON_STYLES = {\r\n  padding: \"15px 25px\",\r\n  background: \"#dd4b39\",\r\n  border: \"0\",\r\n  outline: \"none\",\r\n  cursor: \"pointer\",\r\n  color: \"white\",\r\n  fontWeight: \"bold\",\r\n};\r\n\r\nconst IndexPage = () => {\r\n  return (\r\n    <Form\r\n      method=\"post\"\r\n      action={`/auth/${SocialsProvider.GOOGLE}`}\r\n      style={CONTAINER_STYLES}\r\n    >\r\n      <button style={BUTTON_STYLES}>Login with Google</button>\r\n    </Form>\r\n  );\r\n};\r\n\r\nexport default IndexPage;\r\n```\r\n\r\n![Login Button Homepage](https://ik.imagekit.io/devtoolstech/add-social-auth-remix-run/Screenshot_2022-02-10_at_11.46.32_PM_bfcA9EC6C7Q.png?ik-sdk-version=javascript-1.4.3&updatedAt=1644517018537)\r\n\r\n- Now, let us create the `dashboard` route. It will open on successful user authentication.\r\n\r\n```js\r\n// file: app/routes/dashboard.js\r\n\r\nimport { useLoaderData } from \"remix\";\r\n\r\nimport { authenticator } from \"../services/auth.server.js\";\r\n\r\nconst CONTAINER_STYLES = {\r\n  width: \"100%\",\r\n  height: \"100vh\",\r\n  display: \"flex\",\r\n  justifyContent: \"center\",\r\n  alignItems: \"center\",\r\n  flexDirection: \"column\",\r\n};\r\n\r\nconst BUTTON_STYLES = {\r\n  padding: \"15px 25px\",\r\n  background: \"#dd4b39\",\r\n  border: \"0\",\r\n  outline: \"none\",\r\n  cursor: \"pointer\",\r\n  color: \"white\",\r\n  fontWeight: \"bold\",\r\n};\r\n\r\nexport const loader = async ({ request }) => {\r\n  // authenticator.isAuthenticated function returns the user object if found\r\n  // if user is not authenticated then user would be redirected back to homepage (\"/\" route)\r\n  const user = await authenticator.isAuthenticated(request, {\r\n    failureRedirect: \"/\",\r\n  });\r\n\r\n  return {\r\n    user,\r\n  };\r\n};\r\n\r\nconst Dashboard = () => {\r\n  // getting user from loader data\r\n  const { user } = useLoaderData();\r\n\r\n  // displaying authenticated user data\r\n  return (\r\n    <div style={CONTAINER_STYLES}>\r\n      <h1>You are LoggedIn</h1>\r\n      <p>{user.displayName}</p>\r\n    </div>\r\n  );\r\n};\r\n\r\nexport default Dashboard;\r\n```\r\n\r\n![Dashboard Route](https://ik.imagekit.io/devtoolstech/add-social-auth-remix-run/Screenshot_2022-02-10_at_11.51.42_PM_tTfncd-UU.png?ik-sdk-version=javascript-1.4.3&updatedAt=1644517321136)\r\n\r\n## Adding Logout Functionality\r\n\r\nLet us create a route `logout`. When visited, this route is going to delete the user session and log out the user.\r\n\r\n```\r\ncd app/routes\r\ntouch logout.js\r\n```\r\n\r\n```js\r\n// file: app/routes/logout.js\r\n\r\nimport { authenticator } from \"../services/auth.server.js\";\r\n\r\nexport const action = async ({ request }) => {\r\n  await authenticator.logout(request, { redirectTo: \"/\" });\r\n};\r\n```\r\n\r\nLet us update our `Dashboard` route to show a button for the user to log out.\r\n\r\n```js\r\n// file: app/routes/dashboard.js\r\nimport { useLoaderData, Form } from \"remix\";\r\n\r\n...\r\n\r\nconst Dashboard = () => {\r\n  ...\r\n  // displaying authenticated user data\r\n  return (\r\n    <div style={CONTAINER_STYLES}>\r\n      ...\r\n      <Form action=\"/logout\" method=\"post\">\r\n        <button style={BUTTON_STYLES}>Logout</button>\r\n      </Form>\r\n    </div>\r\n  );\r\n};\r\n\r\n...\r\n```\r\n\r\n![Dashboard with Logout Button](https://ik.imagekit.io/devtoolstech/add-social-auth-remix-run/Screenshot_2022-02-11_at_12.11.53_AM_iq4PWcX9U8h.png?ik-sdk-version=javascript-1.4.3&updatedAt=1644518527164)\r\n\r\nNow, when the user clicks on the `Logout` button then form submission would happen and a request would be made to `/logout` that would eventually delete the user session!\r\n\r\n## Demo\r\n\r\n\r\n<video width=\"100%\" height=\"100%\" controls>\r\n  <source src=\"https://ik.imagekit.io/devtoolstech/add-social-auth-remix-run/social-auth-demo_xBbVBLizp.mp4?ik-sdk-version=javascript-1.4.3&updatedAt=1644520540465\" type=\"video/mp4\">\r\n</video>\r\n\r\n## Live Demo\r\n\r\nYou can check out the live demo here -- [https://devtools.tech/login](https://devtools.tech/login/?ref=social-auth-remix-server-blog-post)\r\n\r\n## Source Code\r\n\r\nYou can find the entire source code for this tutorial here: [https://github.com/Devtools-Tech-Team/remix-run-social-authentication-example](https://github.com/Devtools-Tech-Team/remix-run-social-authentication-example)\r\n\r\nI hope this blog post helped you in some way. Please do share it and show our content much-needed love! :D\r\n\r\nIf you feel something is missing/wrong/improvement is possible then feel free to reach out -- [Devtools Tech](https://twitter.com/devtoolstech) and [Yomesh Gupta](https://twitter.com/yomeshgupta)\r\n","languages":[],"editorConfig":{}},"stats":{"views":18595,"used":0,"likes":0},"description":"","published":true,"isActive":true,"tags":["javascript","remix run","social login","login with google","session","encryption","js fundamentals","remix server"],"slug":"add-social-authentication-to-remix-run-project-or-remix-server---rid---GEN4IbgWorJNeQL7Gkm8","isPremium":false,"categories":[],"requires":[],"_id":"62056636e12bce537863f36a","title":"Add Social Authentication to Remix Run Project | Remix Server","resourceId":"GEN4IbgWorJNeQL7Gkm8","createdAt":1644521014315,"modifiedAt":1644521372553},"currentUser":null,"isOwner":false,"recommendations":{"questions":[{"_id":"5e9c68f1982bd3483e04bcc8","content":{"languages":["javascript"],"difficulty":1},"tags":["node.js","promises","react.js","javascript","frontend","programming","code","interview","frontend fundamentals"],"title":"What would be the output of the following code snippet? | Promise Based Output Question | Part One","questionId":"TQBJFJPPHX4VxH0MrxBt","slug":"what-would-be-the-output-of-the-following-code-snippet-or-promise-based-output-question-or-part-one---qid---TQBJFJPPHX4VxH0MrxBt"},{"_id":"636dfc99bc012474df4d3825","content":{"languages":["javascript"],"difficulty":1},"tags":["javascript","frontend","coding","devtools tech","interview questions","interview preparation","mcq","programming paradigm","tooling","mdn","js paradigm","programming questions","array slice","array methods","javscript arrays"],"slug":"what-would-be-the-output-of-the-following-code-based-on-array-slice---qid---4GsiLnGEEmLaUaoWb64V","title":"What would be the output of the following code? (Based on Array Slice)","questionId":"4GsiLnGEEmLaUaoWb64V"},{"_id":"61f39ebb1238c849f9e151ec","content":{"languages":["react","html"],"difficulty":2},"tags":["react","javascript","frontend interview question","js interview questions","machine coding round","browser api","dom","audio visualisation","backend","data sync"],"slug":"build-an-audio-recorder-from-scratch-or-javascript-interview-question-or-react---qid---K783XlxLzH7bMytThN2m","title":"Build An Audio Recorder From Scratch | JavaScript Interview Question | React","questionId":"K783XlxLzH7bMytThN2m"},{"_id":"6342957642e7761bfac1a52d","content":{"languages":["javascript","typescript"],"difficulty":1},"tags":["javascript","arrays","javascript polyfills","interview question","frontend","coding","devtools tech","programming","codedamn","frontend masters","egghead","razorpay","hotstar","tata1mg","swiggy","zomato","array"],"slug":"how-to-implement-array-prototype-at-javascript-interview-question-or-problem-solving-or-javascript-polyfills---qid---cbVcZ8jIXITU5wD6hIrb","title":"How to implement Array.prototype.at? JavaScript Interview Question | Problem Solving | JavaScript Polyfills","questionId":"cbVcZ8jIXITU5wD6hIrb"},{"_id":"5e8cde7cb2cdcd5697fea7bc","content":{"difficulty":2,"languages":"javascript"},"tags":["node.js","javascript","frontend","programming","new constructor"],"title":"JS Constructor via New Keyword ","questionId":"FJ3qdetSIIobBegZdL1d","slug":"js-constructor-via-new-keyword---qid---FJ3qdetSIIobBegZdL1d"}],"resources":[{"_id":"62d3b928d3473370ac372879","content":{"difficulty":4,"domain":2,"type":2,"isInternal":false},"tags":["javascript","promises","async programming","advanced javascript","interview question","frontend","coding"],"slug":"javascript-promises-fundamentals-every-engineer-should-know-or-part-2-or-event-loop-or-microtasks---rid---oBrzK6Mt7HNAnKBT3ogC","title":"JavaScript Promises Fundamentals Every Engineer Should Know | Part 2 | Event Loop | MicroTasks","resourceId":"oBrzK6Mt7HNAnKBT3ogC"},{"_id":"652d65f9d5ab2876a4ca215c","content":{"difficulty":4,"domain":2,"type":1,"isInternal":false,"languages":["undefined"]},"tags":["frontend","coding","interview","tooling","web tools","ui","ux","atlan","devtools tech"],"slug":"atlan-interview-experience-frontend-engineer-intern---rid---v5soIECQ4OQQ9S1FFR7n","title":"Atlan Interview Experience: Frontend Engineer Intern","resourceId":"v5soIECQ4OQQ9S1FFR7n"},{"_id":"5f1dd74dcbec5f7ffc0c2fac","content":{"difficulty":2,"domain":3,"type":1,"isInternal":true},"tags":["node.js","express","rest","api","framework","backend","tools","devtools","framework development","build express"],"slug":"build-your-own-expressjs-or-part-3---rid---6mHfnu0fQoPw4RBpexRV","title":"Build your own expressjs | Part 3","resourceId":"6mHfnu0fQoPw4RBpexRV"},{"_id":"69831e01895fc3bf013c2a19","content":{"difficulty":4,"domain":2,"type":1,"isInternal":true,"languages":[]},"tags":["javascript","ui","ux","devtools tech","coding","frontend interview","coding rounds","tutorial","frontend interview experience"],"slug":"microsoft-frontend-interview-experience---rid---XTc4tJoPszAFovxvJjRX","title":"Microsoft Frontend Interview Experience","resourceId":"XTc4tJoPszAFovxvJjRX"},{"_id":"69537c54304108b3b5c96b2a","content":{"difficulty":4,"domain":1,"type":1,"isInternal":true,"languages":[]},"tags":["javascript","ui","ux","devtools tech","routing","frontend","coding","programming","tutorial","system design"],"slug":"urls-and-routing-practical-example---rid---o6V68nNKXJEThLaP9wmQ","title":"URLs and Routing - Practical Example","resourceId":"o6V68nNKXJEThLaP9wmQ"}]}}