{"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":18869,"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":"5f521c836d3cda64e470c3eb","content":{"difficulty":4,"languages":"javascript"},"tags":["javascript","prototype","frontend fundamentals","prototype chain"],"slug":"what-would-be-the-output-different-ways-of-prototype-calls---qid---oRjYJXGzxi50NhQNWZ8g","title":"What would be the output? (Different ways of Prototype calls)","questionId":"oRjYJXGzxi50NhQNWZ8g"},{"_id":"6215e6591641361c7c65de90","content":{"difficulty":3,"languages":"javascript"},"tags":["javascript","frontend","coding","architecture","js fundamentals","web","system design"],"slug":"how-would-you-implement-pagination-in-a-frontend-application-or-javascript-interview-questions-or-frontend-architecture---qid---IL8Whw2ayeqmg0kvHQLd","title":"How would you implement pagination in a frontend application? | JavaScript Interview Questions | Frontend Architecture","questionId":"IL8Whw2ayeqmg0kvHQLd"},{"_id":"698cd2ea895fc3bf0142a54f","content":{"languages":["javascript","typescript"],"difficulty":2},"tags":["javascript","ui","ux","devtools tech","questions","frontend interview","google"],"slug":"priority-based-data-fetching---qid---VpqJajJMcGBmxW556YM4","title":"Priority Based Data Fetching","questionId":"VpqJajJMcGBmxW556YM4"},{"_id":"66f167dbe61c855445b30d0d","content":{"languages":["react","html"],"difficulty":4},"tags":["javascript","frontend","ui","ux","coding problem","frontend interview question","coding challenge","bitgo","code","tutorial","blog","interview","frontend interviews"],"slug":"how-to-build-a-transfer-list-ui-component---qid---JXmKtbLuIWiEgODu2MRJ","title":"How to build a Transfer List UI component?","questionId":"JXmKtbLuIWiEgODu2MRJ"},{"_id":"69c6c5807fa48f2af9fefb80","content":{"languages":["html"],"difficulty":1},"tags":["javascript","ui","ux","devtools tech","interview question","atlassian","frontend coding","ui challenges"],"slug":"real-time-comment-feed---qid---gxnubQDhcOkUH6VxaJra","title":"Real-Time Comment Feed","questionId":"gxnubQDhcOkUH6VxaJra"}],"resources":[{"_id":"6990b509d080a1c8aefc6156","content":{"difficulty":4,"domain":1,"type":1,"isInternal":true,"languages":[]},"tags":["javascript","ui","ux","jobs","job search","frontend","coding","devtools tech"],"slug":"how-to-improve-job-search-using-google---rid---JNrLxyNeJ4pMMHD6KnOh","title":"How to improve job search using Google","resourceId":"JNrLxyNeJ4pMMHD6KnOh"},{"_id":"69234e9fbf1a48f85e0d25ce","content":{"difficulty":1,"domain":2,"type":2,"isInternal":false,"languages":[]},"tags":["frontend","coding","ui","ux","devtools tech","frontend performance","tutorial"],"slug":"saved-18kb-of-javascript-with-this-trick---rid---gEEtkB4tWNRiRTcoyOHU","title":"Saved 18kB of JavaScript with THIS Trick","resourceId":"gEEtkB4tWNRiRTcoyOHU"},{"_id":"5f2c0c39f81d6d48b5c4cd06","content":{"difficulty":1,"domain":2,"type":1},"tags":["frontend","html","css","webpage performance","css3","page load times"],"slug":"content-visibility-the-new-css-property-that-boosts-your-rendering-performance---rid---qB5R8n3HZ3NWdukfs7zu","title":"content-visibility: the new CSS property that boosts your rendering performance","resourceId":"qB5R8n3HZ3NWdukfs7zu"},{"_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":"61eeae76c596de5b12fea561","content":{"difficulty":4,"domain":2,"type":1,"isInternal":true},"tags":["remix","js framework","dev tooling","sass","css","styling","frontend"],"slug":"setting-up-sass-with-remix-run---rid---lXDyMjDSdDZDXxNcJ2ep","title":"Setting Up SASS With Remix Run","resourceId":"lXDyMjDSdDZDXxNcJ2ep"}]}}