LICENSE Documentation

Official TypeScript developer resources and documentation for simple-login/LICENSE. Verified technical context for AI.

0 stars TypeScript TypeScript 1 file Β· ~7,975 tokens
RAW Doc

1. Project Overview & Quickstart (simple-login/LICENSE)

LICENSE

Open-source repository simple-login/LICENSE

Repository Details

Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.

2. Official Technical Reference & Guides (simple-login/docs)

File: README.md

To get hugo theme

bash
git submodule update --init --recursive

To deploy:

bash
hugo && vercel --prod

To dev locally

bash
hugo server

Then open http://localhost:1313


File: content/docs/app.md

App

What is a SimpleLogin App?

A SimpleLogin App represents an user database. For ex, if you have iOS, Android and Web app for a single product, then you just need a single SimpleLogin App. Same applies if you have 2 different products but that share the same user database.

Create an App

Please head to https://app.simplelogin.io/developer/ and click on Create new app to create a new app. You only need to provide a name.

All app fields are optional. We recommend however to provide as much information as possible so user can recognize your app. Teaser: a showcase feature is in progress and once the feature is ready, you can submit your app to the showcase to promote it to all SimpleLogin users.

Depending on the flow you choose (code or implicit), you would need store either:

  • both oauth client id and oauth client secret if you decide to use code flow

  • or just oauth client id if you choose implicit flow

To have a shorter terminology, AppID and oauth client id are the same thing and so are AppSecret and oauth client secret.

App Redirect URI

This is the most important field in your app! By default SimpleLogin whitelists all http[s]://localhost:* address to facilitate local development but you need to set your app Redirect URI before deploying your app to production: in effect, SimpleLogin will refuse to present the Authorization Page to user if it doesn't recognize the redirect_uri passed in the url. You can add as many redirect uri as you want. Please note that except the localhost ones, the Redirect URI needs to be https for security reason.

When some errors happen during the authorization flow, SimpleLogin redirects user back to redirect_uri?error={error_code} so your app can act accordingly. Please find the list of all errors on [Error Codes]({{< relref "/docs/errors.md" >}}).

Libraries/Framework

SimpleLogin has added support for SimpleLogin in the following popular libraries and more are coming:

It's impossible to cover all Social Login libraries for all languages. As SimpleLogin implements fully OAuth2/OpenID Connect standard, any library compatible with these standards should be able to handle SimpleLogin with the following OAuth2/OpenID Connect endpoints:

Please find in the Guides section dedicated guide for some frameworks/languages.

If you don’t want to use a library that supports OAuth/OpenID Connect, this can work too! Please head to

  • [Code Flow - The raw way]({{< relref "/docs/code-flow.md" >}}) for implementing the Code Flow
  • [Implicit Flow - The raw way]({{< relref "/docs/implicit-flow.md" >}}) for implementing the Implicit Flow

File: content/docs/code-flow.md

Code Flow - the raw way

If you don't use (or don't want to use a library that supports OAuth/OpenID Connect), you totally can! The protocol is actually quite simple from the app's point of view. (the spec is hundreds pages for provider though ...)

This section will explain how to implement the Code Flow in your back-end along with some info to watch out for.

To implement the Code Flow please make sure you have:

  • read through the [Introduction]({{< relref "/" >}}) to have a rough idea on what is the Code Flow and
  • followed the [App]({{< relref "/docs/app.md" >}}) to have the SimpleLogin AppId and AppSecret.

As recommended in the The Twelve Factors, let's export these values into env variables:

bash
export CLIENT_ID={your_app_id}
export CLIENT_SECRET={your_app_secret}

The way to read these env variables in the code differs from language to language, a quick google read env var {your language} should return some results.

To implement the Code Flow, you would need 2 endpoints:

  1. Login endpoint: that redirects user to SimpleLogin authorization page
  2. Callback endpoint: user is redirected back to this page along with a code that allows you to exchange for an access token which in turn can be used to obtain user info.

Let's jump into each endpoint.

Login endpoint

This endpoint handles user clicking on Sign in with SimpleLogin button (e.g. [Sign in with SimpleLogin](https://your-app/login)) and redirects user to a formatted SimpleLogin url. This url has the following format. Line breaks are added for visibility, please remove them in your code.

text
https://app.simplelogin.io/oauth2/authorize
    ?response_type=code
    &client_id=CLIENT_ID
    &redirect_uri=REDIRECT_URI
    &scope=profile
    &state=STATE

Let's split this long url down to components:

  • response_type=code: indicates the flow. Its value is code for code flow and token for implicit flow. In our case, it's always code.
  • client_id=CLIENT_ID: CLIENT_ID is your SimpleLogin AppID. In OAuth terminology, client is actually your app.
  • redirect_uri=REDIRECT_URI: upon user approval, user will be redirected to this url. This should be the url of your callback endpoint that we are going to create in the next step. If your app is https://my-app.com on production and http://localhost:8000 locally, this url would probably be https://my-app.com/callback when you deploy your app on production. Please make sure to add this redirect url on your SimpleLogin Developer page. (see [App]({{< relref "/docs/app.md" >}}))
  • scope=profile: the user information you want. SimpleLogin supports only profile scope which contains user email, name and avatar.
  • state=STATE: this STATE value should be generated randomly to avoid CSRF attack. This value will be checked in the callback endpoint and one standard way is to save this value into the cookie that you can retrieve later in the callback endpoint.

Callback endpoint

Upon user approval, they will be redirected to this below url. Line breaks are also added here for visibility only, please remove them in your code.

text
REDIRECT_URI
    ?code=CODE
    &state=STATE

Let's break the components down:

  • REDIRECT_URI the same REDIRECT_URI in the login endpoint
  • STATE: the state generated in the login endpoint. It's your app's responsibility to compare this STATE to the one generated in previous step, typically by retrieving it from the cookie.
  • CODE: used to exchange for the access token in a POST request to SimpleLogin:
bash
curl -X POST https://app.simplelogin.io/oauth2/token \
    -F 'grant_type=authorization_code' \
    -F 'code=CODE' \
    -F 'redirect_uri=REDIRECT_URI' \
    -F 'client_id=CLIENT_ID' \
    -F 'client_secret=CLIENT_SECRET'

REDIRECT_URI is sent once again. The CLIENT_ID and CLIENT_SECRET are retrieved from env variable.

SimpleLogin will return this response if everything is correct:

json
{
    "access_token": "LONG_STRING",
    "expires_in": 3600,
    "scope": "profile",
    "token_type": "Bearer",
    "user": {
        "avatar_url": "https://avatar.png",
        "client": "YOUR_APP_NAME",
        "email": "[email protected]",
        "email_verified": true,
        "id": 36,
        "name": "FIRST LAST",
        "sub": "36"
    }
}

With this user info, you should now either be logged in or have access to some reserved information.

Congratulations, you just implemented a OAuth client!


File: content/docs/discourse.md

Add SIWSL (Sign in with SimpleLogin) to Discourse


Discourse is a popular open-source forum software.

After adding SIWSL to Discourse, a person can choose to use their SimpleLogin account to log in or sign up on your forum πŸ‘‡

and they can control which information they want to share:

Adding SIWSL is done in 3 simple steps.

Step 1: Install the OpenID Connect Authentication Plugin

First, let's install the official OpenID Connect Authentication plugin by adding this line

text
sudo -E -u discourse git clone https://github.com/discourse/discourse-openid-connect.git

into your container’s app.yml file (usually present in /var/discourse/containers/). More info on how to install a plugin on How to Install Plugins in Discourse

text
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git
          - sudo -E -u discourse git clone https://github.com/discourse/discourse-openid-connect.git

After that rebuild the container.

bash
cd /var/discourse
./launcher rebuild app

The plugin should be now available on the Discourse Admin/Plugins page


Step 2: Create a website on SimpleLogin dashboard

Head to SimpleLogin dashboard and create a new website.

Now go to the OAuth2 endpoints page:

  • Note the Client ID and Client Secret needed for the next step.

  • Add {your forum url}/auth/oidc/callback as redirect_uri, make sure to replace {your forum url} by your actual forum URL.


Step 3: Config Discourse OpenID Connect Authentication Plugin

Go to the Discourse admin page for Discourse OpenID Connect Authentication Plugin

then make sure to

Lastly, modify the plugin title to "Sign in with SimpleLogin" by editing the text (pointed by the arrow πŸ‘†)

Enjoy!

Now SIWSL should be provided as a login/register option on your forum. Your community members now have a choice of using a privacy-focused, open-source login solution πŸŽ‰.


File: content/docs/errors.md

Possible error codes

When some errors happen during the authorization flow, SimpleLogin redirects user back to redirect_uri?error={error_code} so your app can act accordingly. Some errors are often caught during development but some can come from the user. Please find below the complete list of errors:

  • deny: probably the most frequent error. This happens when user denies sharing their info with your app. You can maybe in this case offer another sign-in mechanism to user, or if SimpleLogin is the only one, let user know that they need to accept sharing their data in order to use your app.

  • invalid_client_id: this happens when the provided client_id is unknown or empty. It can happen when the client_id is not set correctly in the code. As a reminder, the authorization url has the following format:

text
https://app.simplelogin.io/oauth2/authorize
        ?response_type=[code|token]
        &client_id=CLIENT_ID
        &redirect_uri=REDIRECT_URI
        &scope=profile
        &state=STATE
  • http_not_allowed: by default, http[s]://localhost:* is whitelisted to facilitate development. However once the Sign in with SimpleLogin code hits the production, a correct redirect_uri needs to be set on developer page (cf [App]({{< relref "/docs/app.md" >}})). For security reason, http is not allowed so you must use https for the web. For mobile apps, a custom scheme (e.g. com.my_company.my_app://) is also allowed.

  • unknown_redirect_uri: the redirect_uri, if other than http[s]://localhost:* needs to be set on developer page. Please see [App]({{< relref "/docs/app.md" >}}).


File: content/docs/flask.md

Flask

For Flask applications, we recommend Requests-OAuthlib, a library to integrate OAuth providers. This library is also used by social-app-django to add social login buttons to Django apps

The code example is on https://github.com/simple-login/flask-example.

Preparation

First please install Requests-OAuthlib:

bash
pip3 install requests_oauthlib

Then please store your SimpleLogin AppID and AppSecret somewhere, preferably in env variable as recommended in the The Twelve Factors.

bash
export CLIENT_ID={your_app_id}
export CLIENT_SECRET={your_app_secret}

Bootstrap the app

python
import requests_oauthlib, os, flask

# Get SimpleLogin AppID, AppSecret from env vars
CLIENT_ID = os.environ.get("CLIENT_ID")
CLIENT_SECRET = os.environ.get("CLIENT_SECRET")

app = flask.Flask("my-app")
app.secret_key = "my-super-secret"  # for flask.session

# This allows us to test the app using HTTP.
# Please make sure to disable it in production
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

Login endpoint

When user clicks on Sign in with SimpleLogin, user gets redirected to the SimpleLogin authorization page. This is done using the /login endpoint. The state is necessary to defend against CSRF attack.

python
import requests_oauthlib, os, flask

@app.route("/login")
def login():
    sl = requests_oauthlib.OAuth2Session(
        CLIENT_ID,
        # this supposes you are running your app on the default port 5000
        redirect_uri="http://localhost:5000/callback",
    )

    redirect_url, state = sl.authorization_url(
        "https://app.simplelogin.io/oauth2/authorize"
    )

    # State is used to prevent CSRF, keep this for later.
    flask.session["oauth_state"] = state

    return flask.redirect(redirect_url)

Callback endpoint

When user approves sharing data with your app, they get redirected back to the redirect_uri in the previous step. This route is handled by an endpoint that receives the code and exchanges for access token. The access token is then used to exchange for user info:

python
@app.route("/callback")
def callback():
    sl = requests_oauthlib.OAuth2Session(
        CLIENT_ID, state=flask.session.get("oauth_state")
    )
    # Get the "access token"
    sl.fetch_token(
        "https://app.simplelogin.io/oauth2/token",
        client_secret=CLIENT_SECRET,
        authorization_response=flask.request.url,
    )

    user_info = sl.get("https://app.simplelogin.io/oauth2/userinfo").json()

    # This is where you log user in,
    # for ex via flask-login extension: login_user(user)
    return f"""
    Welcome {user_info["name"]} 
    Your email is {user_info["email"]} 
    And your avatar: 
    """

Run the App and enjoy!

Let's run the app

bash
flask run

Now you should be able to SLWSL at http://localhost:5000/login


File: content/docs/frontend-js.md

Frontend JS

For Frontend JS (as opposed to Backend Node.js) you can use the Implicit Flow to log user in.

We recommend using SimpleLogin JS SDK, which is actually a thin layer over hello.js. For this reason, you can also use hello.js if you app already uses this library.

For the web, there are 2 ways to use SimpleLogin in terms of user experience:

  • With popup: when clicking on Sign in with SimpleLogin a new window popup is opened and closed once user approves.

{{< figure src="/images/popup.png" caption="With Popup" >}}

  • Without popup: in the same window/tab user is redirected to SimpleLogin authorization page and returns back to your app once they approves.

We recommend the with popup way so you don't have to deal with state saving when browser reloads in without popup.

By default, the SimpleLogin JS SDK uses the with popup way.

Please find below different ways to add SimpleLogin into your webapp:

SimpleLogin JS SDK

First include it in your html:

html
<script src="https://simplelogin.io/sdk/sdk.js"></script>

In your js, you need to init the SDK with your SimpleLogin AppID:

js
SL.init("{your SimpleLogin AppID}");

To open the login popup, use SL.login() which takes a callback as parameter: the callback will receive user info when they approves on SimpleLogin authorization page.

js
SL.login(function(user) {
    console.log("got user from SL SDK", user);
})

The SL.login is typically called inside a function onclick:

js
document.getElementById("btn-simplelogin").onclick = function(e) {
  SL.login(function(user) {
      console.log(user)
  })
}

OIDC Client

oidc-client adds OpenID Connect (OIDC) and OAuth2 protocol support for browser-based JavaScript applications.

This library is behind redux-oidc for ReactJS / Redux, vuejsoidcclient for Vue.js, Angular2OidcClient for Angular, so if you use these web libraries, adding SimpleLogin should be a matter of changing authority setting to https://app.simplelogin.io/.

oidc-client is quite a low-level library and offers a lot of flexibility. It separates the login step and the callback step.

Let's start by creating a index.html:

html
start signin
end signin

<script src="https://cdn.jsdelivr.net/gh/IdentityModel/[email protected]/dist/oidc-client.min.js"></script>

And add the js code, make sure to replace {your SimpleLogin AppId} by your AppID.

js
var settings = {
    // oidc-client will discover all OIDC settings automatically,
    // via https://app.simplelogin.io/.well-known/openid-configuration
    authority: "https://app.simplelogin.io/",
    client_id: "{your SimpleLogin AppId}",
    response_type: 'id_token token',
    // openid in scope to indicate this is OpenID Connect
    scope: 'openid',
    // You need to update this redirect_uri when deploying to production
    redirect_uri: "http://localhost:8000",
}
var mgr = new Oidc.UserManager(settings);

function startSignin() {
    mgr.signinRedirect({state:"some data"}).then(function() {
        console.log("signinRedirect done");
    });
}

function endSignin() {
    mgr.signinRedirectCallback().then(function(user) {
        console.log("signed in", user);
    });
}

Now if you run the a local web server (using python3 -m http.server for example), you should be able to sign in with SimpleLogin when you open http://localhost:8000


File: content/docs/implicit-flow.md

Implicit Flow - the raw way

If you don't use (or don't want to use a library that supports OAuth/OpenID Connect), it's OK! The protocol is actually quite simple as you will see shortly below.

This section will explain how to implement the Implicit Flow in your front-end using Vanilla JS.

Please make sure you have:

  • read through the [Introduction]({{< relref "/" >}}) to have a rough idea on what is the Implicit Flow and
  • followed the [App]({{< relref "/docs/app.md" >}}) to have the SimpleLogin AppId. You don't need the AppSecret in this flow.

To implement the Implicit Flow, you would need 2 parts in your app:

  1. Login part: that redirects user to SimpleLogin authorization page
  2. Callback part: user is redirected back to your app along with an access token which can be used to obtain user info.

Let's jump into each part.

Login part

This part handles user clicking on Sign in with SimpleLogin button (e.g. Login with SimpleLogin) and redirects user to a formatted SimpleLogin url. This url has the following format. Line breaks are added for visibility, please remove them in your code.

text
https://app.simplelogin.io/oauth2/authorize
    ?response_type=token
    &client_id=CLIENT_ID
    &redirect_uri=REDIRECT_URI
    &scope=profile
    &state=STATE

Let's take a closer look at this long url:

  • response_type=token: indicates the flow. Its value is token for implicit flow and code for code flow. In our case, it's always token.
  • client_id=CLIENT_ID: CLIENT_ID is your SimpleLogin AppID. In OAuth terminology, client is actually your app.
  • redirect_uri=REDIRECT_URI: upon user approval, user will be redirected to this url. This is usually the same as your app url. By default SimpleLogin whitelists localhost so you don't need to do anything for local development. Please see [App]({{< relref "/docs/app.md" >}}) for more information.
  • scope=profile: SimpleLogin supports only profile scope which contains user email, name and avatar.
  • state=STATE: this STATE value should be generated randomly to avoid CSRF attack. This value will be checked in the callback endpoint and one standard way is to save this value into the sessionStorage that you can retrieve later in the callback endpoint.

Callback part

Upon user approval, they will be redirected to this url below. Line breaks are also added here for visibility only, please remove them in your code.

text
REDIRECT_URI
    #state=STATE
    &scope=profile
    &access_token=ACCESS_TOKEN

Let's break the components down:

  • REDIRECT_URI the same REDIRECT_URI in the previous step
  • STATE: the state generated in the previous step. It's your app's responsibility to compare this STATE to the one generated in previous step, typically by retrieving it from the sessionStorage.
  • ACCESS_TOKEN: used to get user info in a POST request to SimpleLogin. You might also notice that # is used instead of ?; this is to avoid the access token hitting your back-end (if there's any) in the implicit flow.

Here's the code snippet using fetch to get user info with the obtained access token:

js
fetch("https://app.simplelogin.io/oauth2/user_info", {
    method: 'GET',
    headers: {
        'AUTHORIZATION': `Bearer ${ACCESS_TOKEN}`
    }
}).then(response => response.json()).then(userInfo => {
    console.log(userInfo);
    alert(`Welcome ${userInfo.name}`);
})

SimpleLogin will return this response if everything is correct:

json
{
    "avatar_url": "https://avatar.png",
    "client": "{Your App Name}",
    "email": "[email protected]",
    "email_verified": true,
    "id": 36,
    "name": "FIRST LAST",
    "sub": "36"
}

With this user info, you should now either be logged in or have access to some reserved information.

Congratulations, you just implemented the OAuth Implicit Flow!

Code example

Please find below a full code example for this section. Please make sure to replace YOUR_APP_ID by your SimpleLogin AppID and serve the code on port 8000 (or just replace http://localhost:8000 by your local url). This can be done by saving this code into a index.html file and serve the file with Python http module:

python3 -m http.server

or by NodeJS static-server tool, can be installed via npm -g install static-server

static-server -p 8000

html
Sign in with SimpleLogin

<script>
    function simpleLogin() {
        let appId = "YOUR_APP_ID";
        let state  = randomString();

        // save state into sessionStorage to check later in the callback
        sessionStorage.setItem("state", state);

        let url = "https://app.simplelogin.io/oauth2/authorize" +
            "?response_type=token" +
            `&client_id=${appId}` +
            "&redirect_uri=http://localhost:8000" +
            "&scope=profile" +
            `&state=${state}`

        // redirect user to SimpleLogin
        location.href = url;
    }

    // if accessToken is found, then it's the callback
    let accessToken = getQueryParam("access_token");

    if (accessToken) {
        // Check if state has changed
        state = getQueryParam("state");

        if (state != sessionStorage.getItem("state")){
            // CSRF attack
            alert("nice try! Or you need to enable sessionStorage ...");
        }

        // get user info
        fetch("https://app.simplelogin.io/oauth2/user_info", {
            method: 'GET',
            headers: {
                'AUTHORIZATION': `Bearer ${accessToken}`
            }
        }).then(response => response.json()).then(userInfo => {
            console.log(userInfo);
            alert(`Welcome ${userInfo.name}`);
        })
    }

    // utility function to get query value from url
    function getQueryParam(key) {
        var half = location.href.split(key + '=')[1];
        return half !== undefined ? decodeURIComponent(half.split('&')[0]) : null;
    }

    // generate a 15-char string
    function randomString(){
        return Math.random().toString(36).slice(-15);
    }

</script>

You might notice that when clicking on Login with Facebook/Google button, a new (small) window is opened instead of user leaving your app. This has the advantage of user still staying on your app and therefore you don't need to worry about saving user data before redirecting them to another page.

This can be done quite easily using SimpleLogin SDK JS (see [Frontend-JS]({{< relref "/docs/frontend-js.md" >}})) or using libraries such as hello.js or oidc-client. You can also implement this mechanism yourself using the postMessage method. How to implement this is out of scope of this page as it requires some advanced technics but basically that's how it works:

  • When user clicks on Sign in with SimpleLogin, you open a new window using the window.open method. You can use the current page as target but usually people use an empty page, let's say redirect.html to speed up the load time:
js
const authWindow = window.open(
        "/redirect.html",'SimpleLoginPopup',
        {}
);

On this redirect.html, we have the 2 steps login and callback implemented like in the previous section. The only thing that changes is access token or user information will be sent to the opener page, which is your app via window.opener.postMessage:

js
window.opener.postMessage(
  { auth: { token: access_token } },
  window.opener.location
);

On your app, you need to setup a event listener that handle this message

js
window.addEventListener('onmessage', e => {
    if (e.data.auth) {
        // Get user info with the access_token
    }
})

Please take a look at this gist that contains some snippets that can be used to handle data exchange between your app (the main window) and the popup.


File: content/docs/passport.md

Node.js - Passport

For Node.js application, we recommend passport, an authentication middleware for Node.js. Passport supports a comprehensive set of strategies including username/password login and social login (Facebook, Twitter, etc).

For SimpleLogin, you can use either OAuth strategy or OpenId Connect strategy. In this guide, we will use the OpenId Connect Strategy. The code along with a step-by-step guide is also available on https://github.com/simple-login/passportjs-example

Preparation

First please install passport, passport-openidconnect, express-session (passport uses session to store its data)

bash
npm install passport passport-openidconnect express-session --save

Then please store your SimpleLogin AppID and AppSecret somewhere, preferably in env variable as recommended in the The Twelve Factors.

bash
export CLIENT_ID={your_app_id}
export CLIENT_SECRET={your_app_secret}

Bootstrap the app:

js
var express = require('express');

// For Passport
var session = require('express-session');
var passport = require('passport');
var OidcStrategy = require('passport-openidconnect').Strategy;

var app = express();

// passportjs use session to store user info
app.use(session({
  secret: 'my-super-secret',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

Config SimpleLogin OIDC Provider

Let's tell passport SimpleLogin OIDC (OpenID Connect) setting:

js
// config SimpleLogin OIDC (OpenID Connect) provider
passport.use('SimpleLogin', new OidcStrategy({
  // SimpleLogin OIDC Settings
  issuer: 'https://app.simplelogin.io',
  authorizationURL: 'https://app.simplelogin.io/oauth2/authorize',
  tokenURL: 'https://app.simplelogin.io/oauth2/token',
  userInfoURL: 'https://app.simplelogin.io/oauth2/userinfo',
  // SimpleLogin App Credential from env
  clientID: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  // you might need to change the callbackURL when deploying on production
  callbackURL: 'http://localhost:3000/callback',
  // openid needs to be in scope
  scope: 'openid'
}, (issuer, sub, profile, accessToken, refreshToken, done) => {
  return done(null, profile);
}));

Login endpoint

When user clicks on Sign in with SimpleLogin, user gets redirected to the SimpleLogin authorization page. This is done using the /login endpoint. The nice thing about passport and its OpenID Connect strategy is it also takes care of generating a random state in the redirection URL. The state is necessary to defend against CSRF attack.

js
// redirect user to authorization page
app.use('/login', passport.authenticate('SimpleLogin'));

Callback endpoint

When user approves sharing data with your app, they get redirected back to the redirect_uri in the previous step. This route is handled by an endpoint that receives the code and exchanges for access token. The access token is then used to exchange for user info. Passport once again handles all these details nicely:

js
// user is redirected back
app.use('/callback',
  passport.authenticate('SimpleLogin', {
    failureRedirect: '/error'
  }),
  (req, res) => {
    var user= req.user._json
    res.send(`
      Welcome ${user.name}! 
      Your email: ${user.email} 
      Avatar: 
      `)
  }
);

Run the App and enjoy!

let's finish by adding this to the end:

js
app.listen(3000, () => console.log(`App listening`))

And run the app

bash
node app.js

Now you should be able to sign in with SimpleLogin at http://localhost:3000/login