# Implementing OAuth2 Login with GitHub and Platformatic

OAuth 2 is a popular protocol for authentication and authorization, allowing users to authorize access to their data without the need to share sensitive passwords and other credentials with third-party applications.

While this makes Oauth 2 a great option for implementing single sign-on (SSO) in your Platformatic application, implementing OAuth 2 and configuring it correctly can be a complex task, especially when integrating it with different frameworks and applications.

In this article, we will delve into the implementation of OAuth2 Login with a Platformatic application and GitHub, leveraging the power of OAuth 2.0 in handling access tokens, securely authenticating users and managing resource access.

# **Brief Insights Into OAuth2**

[OAuth2](https://oauth.net/2/) is an authorization framework that revolutionized how applications obtain limited access to user accounts on various HTTP services. Popular platforms such as Facebook and GitHub rely on OAuth2 to delegate user authentication to the hosting service and authorize third-party applications to access user accounts securely.

OAuth 2.0, also known as "Open Authorization," emerged as the industry standard for online authorization in 2012, replacing its predecessor, OAuth 1.0. It empowers websites and applications to access resources hosted by other web apps on behalf of users, without compromising their credentials. By providing consented access and controlling the actions performed by client applications, OAuth 2.0 ensures secure and granular access to user resources.

The key principles of OAuth 2.0 revolve around authorization rather than authentication, with OAuth 2.0 serving as a means of granting access to resources, such as remote APIs or user data. OAuth 2.0 utilizes Access Tokens, which represent the authorization granted to access resources on behalf of end-users.

Although no specific format is defined for Access Tokens, the JSON Web Token (JWT) format is commonly used, allowing token issuers to include data within the token itself. Additionally, Access Tokens may have expiration dates for enhanced security.

## **Platformatic DB: An Overview**

Platformatic DB is designed to simplify the creation of CRUD APIs, eliminating the need to write code manually. It offers the capability to generate both OpenAPI and GraphQL schemas directly from your database, providing a streamlined approach to API development.

Platformatic DB stands out from similar tools by offering customization options through Node.js and Fastify plugins, leveraging the integration with Platformatic Service.

With Platformatic DB, you can eliminate the hassle of creating Create-Read-Update-Delete (CRUD) APIs from scratch and automatically generate OpenAPI/REST API and GraphQL API based on your SQL schema, saving valuable development time.

The tool supports multiple databases, including;

* SQLite
    
* MySQL
    
* MariaDB
    
* PostgreSQL
    

# **Setting up the Required Dependencies**

Before implementing OAuth2 login with GitHub in your Platformatic application, you will need to set up the necessary dependencies. These dependencies will enable your application to handle the OAuth2 flow and interact with GitHub's authentication endpoints. Follow these steps to get started:

## **Create a New Platformatic Project**

To create a new Platformatic project using the Platformatic CLI;

1\. Open your terminal and run the following command:

```plaintext
npm create platformatic@latest
```

2\. The CLI will initiate an interactive command-line tool to guide you through the project setup process. For this guide, select the following:

```plaintext
- Which kind of project do you want to create? => DB
- Where would you like to create your project? => Github-Oauth
- Do you want to create default migrations? => Yes
- Do you want to create a plugin? => Yes
- Do you want to use TypeScript? => No
- Do you want to install dependencies? => Yes (this may take a while)
- Do you want to apply the migrations? => No
- Do you want to generate types? => Yes
- Do you want to create the GitHub action to deploy this application to Platformatic Cloud dynamic workspace? => No
- Do you want to create the GitHub action to deploy this application to Platformatic Cloud static workspace? => No
```

Once the wizard completes, you will have a Platformatic app project created in the "Github-Oauth" folder. It will include sample migration files and a sample plugin script.

To navigate to the project directory using the VS Code terminal, you can use the following command:

```plaintext
cd Github-Oauth
```

Use the following command to apply migrations to the project:

```plaintext
npx plt db migrations apply
```

Run the following command to install the necessary dependencies in the project:

```plaintext
npm i --save fastify@4.19.2 @platformatic/sql-mapper@0.30.0 @platformatic/sql-graphql@0.30.0
```

## **Include the Fastify OAuth2 Library in your Project**

Use a package manager like npm to install an OAuth2 library for your Platformatic project. We will use the [fastify-oauth2](https://github.com/fastify/fastify-oauth2) library, which is a wrapper of the `simple-oauth2` library. To install, run this command on your terminal:

```plaintext
npm i @fastify/oauth2
```

# **Configuring GitHub as an OAuth2 Provider**

To enable OAuth2 login with GitHub in your Platformatic application, you will need to configure GitHub as an OAuth2 provider. This involves creating a GitHub OAuth application and obtaining the necessary credentials (Client ID and Client Secret) for your application to authenticate with GitHub.

Follow these steps to configure GitHub as an OAuth2 provider:

**1\. Create a GitHub OAuth Application:**

* Log in to your GitHub account and go to the "Settings" page.
    
* In the left sidebar, click on "Developer settings" and then select "OAuth Apps."
    

![](https://lh6.googleusercontent.com/tYtODnxEQnl7kW2K_tRM_QZO-Q7ly0CCNU9aXywBNAG-9rhd6-tEVkpaFFxOSm7Tfy2tA1XwdoImYpvS871_pDWv9yPllq8aeP8zHr7ljnlNaTM1WJUy9zeMmS8thhHXMMWvAeFmaNZp6NRKr_YGu7c align="left")

* Click on the "New OAuth App" button to create a new OAuth application.
    

![](https://lh6.googleusercontent.com/qrsr-EEeahenSBqOT8lkSEhJhyvmVqinVfV-L49DfZ_tdUKqz4HqY-LglQD_PoAu9GrCVlOalkpVgu-yqW7HDuSB500X-9coQgx0irrdADhjN0UWmU2UGkysyv-f-yjONSjysw0FqYLKfg7Ruh0dR6c align="left")

**2\. Provide Application Details:**

* Fill in the required details for your OAuth application, such as the application name, homepage URL, and callback URL.
    

![](https://lh6.googleusercontent.com/1BVeazH3hB1PFsOWV_bv9SLG5YgmNaNFbpBfJG50TNDSzvSDuPxqsGCc4M16PuQKC7qg6_ekQhglUu_gC8fP503VrSJ3G0WQ6j-mcZGSiwS4CTBlbpQ26YizRM90-eS01Ttzgt1HCpUnqujOAjsLerY align="left")

* The callback URL is the URL where GitHub will redirect users after successful authentication. In this case, it should point to the endpoint in your Platformatic application that handles the callback.
    
* Use the following values for the homepage URL and callback URL respectively `“`[`http://127.0.0.1:3042/”`](http://127.0.0.1:3042/%E2%80%9D) and `“`[`http://127.0.0.1:3042/login/github/callback”`](http://127.0.0.1:3042/login/github/callback%E2%80%9D).
    

**3.** **Obtain Client ID and Client Secret:**

* GitHub will provide you with a Client ID and Client Secret after creating the OAuth application.
    
* These credentials are unique to your application and must be kept confidential. Do not share them publicly or expose them in your application's client-side code.
    
* *Note: Keep your Client ID and Client Secret close as we would be needing them in the next section of the article.*
    

# **Building The OAuth2 Request**

We have to build the request to the server which will authorize our service as a granted client. To achieve this, head over to your IDE and navigate to the Github-Oauth directory. In the `plugin.js` file, add the following code:

```javascript
/// <reference path="./global.d.ts" />
"use strict";

const fastifyOauth2 = require("@fastify/oauth2");

/** @param {import('fastify').FastifyInstance} app */
module.exports = async function (app, options) {

  const startRedirectPath = `/login/github`;
  // TODO understand why it is using a full URL
  const callbackUri = `http://127.0.0.1:3042/login/github/callback`;

  await app.register(fastifyOauth2, {
    name: github,
    credentials: {
      client: {
        id: options.clientId,
        secret: options.clientSecret,
      },
      auth: fastifyOauth2.GITHUB_CONFIGURATION,    
    },
    startRedirectPath,
    callbackUri,
  });

  app.get(`/login/github/callback`, async function (request, reply) {
    const { token } = await this.github.getAccessTokenFromAuthorizationCodeFlow(request);

    console.log(`token.access_token`);

    return { access_token: token.access_token };
  });
};
```

Navigate to your “platformatic.db.json” config file and replace its content with the following :

```json
 "$schema": "https://platformatic.dev/schemas/v0.29.0/db",
  "server": {
    "hostname": "{PLT_SERVER_HOSTNAME}",
    "port": "{PORT}",
    "logger": {
      "level": "{PLT_SERVER_LOGGER_LEVEL}"
    }
  },
  "db": {
    "connectionString": "{DATABASE_URL}",
    "graphql": true,
    "openapi": true
  },
  "watch": {
    "ignore": [
      "*.sqlite",
      "*.sqlite-journal"
    ]
  },
  "migrations": {
    "dir": "migrations"
  },
  "plugins": {
    "paths": [{
      "path": "plugin.js",
      "options": {
        "clientId": "YOUR_CLIENT_ID",
        "clientSecret": "YOUR_CLIENT_SECRET"
      }
    }]
  },
  "types": {
    "autogenerate": true
  }
```

Now, when you run `npm start` in your terminal to start your Platformatic application server, you should see a URL as your [localhost](http://localhost)  listening on port 3042.

![](https://lh3.googleusercontent.com/DMa-DvTbX52zkG7UuSwJljtbscLyN-baTNuSumupSRiLne0vNqyV8fP2YLi8pD5bH4_-EKBDEl30mwzxjoQBq__vlGBN4t5e8GuPBrqrg-CA0t47n4rSSS89zT1zofBKmrD18_OTeCrxu-Rc7_wGFB8 align="left")

**ctrl** + **click** the link to open the link in your browser which will redirect you to a Platformatic landing page.

![](https://lh6.googleusercontent.com/1gKC0x_b4tKioTtcFYOYZZIisGcboGpQS3268J4p8pXSgtTBnNkf3i0xSWB6Wjjs0yq1d5UcMw6sjH0z2RViHuUJabcJ7fqJPJo4G84wuQM1kabt-yKvVY3NphXZNw4TWcCtZnd2BaCrfMKvVnZiNZY align="left")

## **Retrieving a Public Access-Token**

On the address bar of your browser, navigate to the URL- “`127.0.0.1:3042/login/github`” which we specified as the `startRedirectPath` in the `plugin.js` file. You will be directed to a GitHub login modal where you will need to input your login credentials.

![](https://lh5.googleusercontent.com/Ps42yppctvLg6lsHCc-MgjvoEhHRz7eccM5qAfZPPYLEkOVO-q5hc5noNLU6yU9yNRCPjKlstKLO1d9a8GyzH1Rgr7kNeKxuDb-iN-PDeG97NypVucehYd_GmALtLTdwvYfwUdXlyAnRLqgnJRR8qzo align="left")

Upon successful login, you will be redirected and your public access token will be returned on the browser page. It is simply a token that contains all the data about your GitHub profile that is publicly accessible– it should look similar to this:

```javascript
/// <reference path="./global.d.ts" />
"use strict";

const fastifyOauth2 = require("@fastify/oauth2");
const { request } = require("undici");

async function getGitHubUserDetails(token) {
  const res = await request("https://api.github.com/user", {
    headers: {
      "User-Agent": "platformatic-authentication-server",
      Accept: "application/vnd.github+json",
      Authorization: `Bearer ${token.access_token}`,
      "X-GitHub-Api-Version": "2022-11-28",
    },
  });

  return res.body.json();
}

/** @param {import('fastify').FastifyInstance} app */
module.exports = async function (app, options) {

  const startRedirectPath = `/login/github`;
  // TODO understand why it is using a full URL
  const callbackUri = `http://127.0.0.1:3042/login/github/callback`;

  await app.register(fastifyOauth2, {
    name: “github”,
    credentials: {
      client: {
        id: options.clientId,
        secret: options.clientSecret,
      },
      auth: fastifyOauth2.GITHUB_CONFIGURATION,
    },
    Scope: ["user:email", "read:user"],
    startRedirectPath,
    callbackUri,
  });

  app.get(`/login/github/callback`, async function (request, reply) {
    const { token } = await this.github.getAccessTokenFromAuthorizationCodeFlow(request);

    const user = await getUserDetails(token);

    return { user };
  });
};
```

The updated code provided for the plugin.js file will now return user data from GitHub. Replace the placeholders **YOUR\_CLIENT\_ID** and **YOUR\_CLIENT\_SECRET** with your Client ID and Client Secret.

The `getGitHubUserDetails` function has now been added, retrieving user details from the GitHub API using the access token. It sends an authenticated request to the GitHub /user endpoint, including the access token in the Authorization header. The response body is returned as JSON.

In the `app.get` function for the callback route, after obtaining the access token, the getUserDetails function is called to retrieve the user data from GitHub.

The user object is returned as the response, which contains the user details retrieved from the GitHub API.

![](https://lh6.googleusercontent.com/6eN4ytkRYtC5pb1ZqyKPiudoD-jFqhzEmZ3AB44Zo20sgXyL6ak710OgQDVqsJitYALe5JAvO28UnoALgfb9JlhVRfLILuIbQxOlAjlPsp1--8Q9jsOVRWHrGyubMnDWjZw8UN9RD2KW0pvgyTSGHy0 align="left")

## **Filtering the Response Data**

To filter the returned user data from the public access token, we will need to make some changes to our `plugin.js file`. Replace the already existing code with the code below:

```javascript
/// <reference path="./global.d.ts" />
"use strict";

const fastifyOauth2 = require("@fastify/oauth2");
const { request } = require("undici");

async function getGitHubUserDetails(token) {
  const res = await request("https://api.github.com/user", {
    headers: {
      "User-Agent": "platformatic-authentication-server",
      Accept: "application/vnd.github+json",
      Authorization: `Bearer ${token.access_token}`,
      "X-GitHub-Api-Version": "2022-11-28",
    },
  });

  const user = await res.body.json();

  return {
    fullName: user.name,
    email: user.email,
  };
}

/** @param {import('fastify').FastifyInstance} app */
module.exports = async function (app, options) {

  const startRedirectPath = `/login/github`;
  // TODO understand why it is using a full URL
  const callbackUri = `http://127.0.0.1:3042/login/github/callback`;

  await app.register(fastifyOauth2, {
    name: ’github’,
    credentials: {
      client: {
        id: options.clientId,
        secret: options.clientSecret,
      },
     auth:fastifyOauth2.GITHUB_CONFIGURATION,
    },
    scope: ["user:email", "read:user"],
    startRedirectPath,
    callbackUri,
  });

  app.get(`/login/github/callback`, async function (request, reply) {
    const { token } = await this.github.getAccessTokenFromAuthorizationCodeFlow(request);

    const user = await getGitHubUserDetails(token)

    return { user };
  });
};
```

The `getGitHubUserDetails` function remains the same, retrieving user details from the GitHub API.

After receiving the response from the GitHub API, the code filters the user data and creates a customized response object.

In this example, the response includes the user's full name and email. You can modify this according to your specific requirements.

## **Storing User Login Data**

To store user login data in a database, we can update the existing code in the `plugin.js` file. Replace the existing code with the following:

```javascript
/// <reference path="./global.d.ts" />
"use strict";

const fastifyOauth2 = require("@fastify/oauth2");
const { request } = require("undici");

async function getGitHubUserDetails(token) {
  const res = await request("https://api.github.com/user", {
    headers: {
      "User-Agent": "platformatic-authentication-server",
      Accept: "application/vnd.github+json",
      Authorization: `Bearer ${token.access_token}`,
      "X-GitHub-Api-Version": "2022-11-28",
    },
  });

  const user = await res.body.json();

  return {
    fullName: user.name,
    email: user.email,
  };
}

/** @param {import('fastify').FastifyInstance} app */
module.exports = async function (app, options) {

  const startRedirectPath = `/login/github`;
const callbackUri = `http://127.0.0.1:3042/login/github/callback`;

  await app.register(fastifyOauth2, {
    name: “github”,
    credentials: {
      client: {
        id: "YOUR_CLIENT_ID",
        secret: "YOUR_CLIENT_SECRET",
      },
      auth: fastifyOauth2.GITHUB_CONFIGURATION,
    },
    scope: ["user:email", "read:user"],
    startRedirectPath,
    callbackUri,
  });

  app.get(`/login/github/callback`, async function (request, reply) {
    const { token } = await this.github.getAccessTokenFromAuthorizationCodeFlow(request);

    const user = await getGitHubUserDetails(token);

    const dbUsers = await app.platformatic.entities.user.find({ 
      where: {
        providerId: { eq: user.providerId }
      }
   });
     
if (dbUsers.length === 0) {
      await app.platformatic.entities.user.save({ input: user });
    }

    return { user };
  });
};
```

Then paste the following code into the [**001.do**](http://001.do)**.sql** file:

```sql
-- Add SQL in this file to create the database tables for your API
CREATE TABLE user (
  id INTEGER PRIMARY KEY,
  fullName TEXT,
  email TEXT,
  token OBJECT,
)
```

Run the command **npx plt db migrations apply** to apply migrations to the project. Then, run `npm start` in your terminal to start your Platformatic application server. In the updated code, we have added a few additional lines to store user data in a database:

* We fetch the user details from the GitHub API as before.
    
* If no matching users are found, we use the [`app.platformatic.entities.user.save`](http://app.platformatic.entities.user.save) method to save the user data to the database.
    

Make sure to replace "**YOUR\_CLIENT\_ID**" and "**YOUR\_CLIENT\_SECRET**" in the code with your actual **GitHub OAuth2 client ID** and **ClientSecret**.

By implementing these changes, the user data will be stored in your Platformatic application's database. You can access the stored user data by navigating to the user URL [http://127.0.0.1:3042/user](http://127.0.0.1:3042/user) where users are stored.

# **Debugging SQLite Migration Errors**

If you encounter an SQLite error while running migrations in your Platformatic application, there is a simple debug solution. First, run the following command in your terminal:

```plaintext
npx plt db migrations apply -t 0
```

This command will apply the initial migration and set the migration target to zero. Once this step is complete, you can run your migrations again using:

```plaintext
npx plt db migrations apply
```

Finally, start your server to ensure that the migrations are successfully applied and your application is running smoothly. This debug process helps resolve SQLite-related migration errors, ensuring the proper functioning of your Platformatic application.

# **Wrapping Up**

Throughout this article, we have explored the principles of OAuth2, learned how to configure GitHub as an OAuth2 provider, and demonstrated the steps to build an OAuth2 request. We have also covered how to store user login data in a database for further use.

You can refer to this [GitHub repository](https://github.com/johnfawole/Github-Oauth2) to enhance your understanding and explore practical implementation details. This repo provides a comprehensive example of integrating OAuth2 login with GitHub and Platformatic, complete with code samples and additional resources.

Implementing OAuth2 with GitHub and Platformatic offers a secure and convenient way to authenticate users and access protected resources. Remember to adapt the implementation to your specific use cases and security requirements. With OAuth2, you can build robust and scalable applications that provide a seamless and trusted user experience.
