1. Project Overview & Quickstart (simple-login/LICENSE)
LICENSE
Open-source repository simple-login/LICENSE
Repository Details
- Repository: simple-login/LICENSE
- Primary Language: Code
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
git submodule update --init --recursiveTo deploy:
hugo && vercel --prodTo dev locally
hugo serverThen 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 idandoauth client secretif you decide to usecode flowor just
oauth client idif you chooseimplicit 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:
- Vanilla JS (
Implicit Flow): hello.js, JSO - Python (Flask and other non-Django frameworks): Requests-OAuthlib
- Django: social-auth-app-django
- NodeJS Passport
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:
- Authorization endpoint: https://app.simplelogin.io/oauth2/authorize
- Token endpoint: https://app.simplelogin.io/oauth2/token
- UserInfo endpoint: https://app.simplelogin.io/oauth2/userinfo
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 Flowand - 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:
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:
- Login endpoint: that redirects user to SimpleLogin authorization page
- Callback endpoint: user is redirected back to this page along with a
codethat allows you to exchange for anaccess tokenwhich in turn can be used to obtainuser 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.
https://app.simplelogin.io/oauth2/authorize
?response_type=code
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=profile
&state=STATELet's split this long url down to components:
response_type=code: indicates the flow. Its value iscodeforcode flowandtokenforimplicit flow. In our case, it's alwayscode.client_id=CLIENT_ID:CLIENT_IDis your SimpleLogin AppID. In OAuth terminology,clientis 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 onlyprofilescope which contains user email, name and avatar.state=STATE: thisSTATEvalue 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.
REDIRECT_URI
?code=CODE
&state=STATELet's break the components down:
REDIRECT_URIthe sameREDIRECT_URIin the login endpointSTATE: the state generated in the login endpoint. It's your app's responsibility to compare thisSTATEto the one generated in previous step, typically by retrieving it from the cookie.CODE: used to exchange for theaccess tokenin a POST request to SimpleLogin:
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:
{
"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
sudo -E -u discourse git clone https://github.com/discourse/discourse-openid-connect.gitinto 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
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.gitAfter that rebuild the container.
cd /var/discourse
./launcher rebuild appThe 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/callbackas 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
- enable OpenID Connect
- copy https://app.simplelogin.io/.well-known/openid-configuration in openid connect discovery document
- copy your Client ID in step 2 to openid connect client id
- Copy your Client Secret in step 2 to openid connect client secret
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 providedclient_idis unknown or empty. It can happen when theclient_idis not set correctly in the code. As a reminder, the authorization url has the following format:
https://app.simplelogin.io/oauth2/authorize
?response_type=[code|token]
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=profile
&state=STATEhttp_not_allowed: by default,http[s]://localhost:*is whitelisted to facilitate development. However once the Sign in with SimpleLogin code hits the production, a correctredirect_urineeds to be set on developer page (cf [App]({{< relref "/docs/app.md" >}})). For security reason,httpis not allowed so you must usehttpsfor the web. For mobile apps, a custom scheme (e.g.com.my_company.my_app://) is also allowed.unknown_redirect_uri: theredirect_uri, if other thanhttp[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:
pip3 install requests_oauthlibThen please store your SimpleLogin AppID and AppSecret somewhere, preferably in env variable as recommended in the The Twelve Factors.
export CLIENT_ID={your_app_id}
export CLIENT_SECRET={your_app_secret}Bootstrap the app
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.
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:
@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
flask runNow 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:
<script src="https://simplelogin.io/sdk/sdk.js"></script>In your js, you need to init the SDK with your SimpleLogin AppID:
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.
SL.login(function(user) {
console.log("got user from SL SDK", user);
})The SL.login is typically called inside a function onclick:
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:
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.
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 Flowand - followed the [App]({{< relref "/docs/app.md" >}}) to have the SimpleLogin
AppId. You don't need theAppSecretin this flow.
To implement the Implicit Flow, you would need 2 parts in your app:
- Login part: that redirects user to SimpleLogin authorization page
- Callback part: user is redirected back to your app along with an
access tokenwhich can be used to obtainuser 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.
https://app.simplelogin.io/oauth2/authorize
?response_type=token
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=profile
&state=STATELet's take a closer look at this long url:
response_type=token: indicates the flow. Its value istokenforimplicit flowandcodeforcode flow. In our case, it's alwaystoken.client_id=CLIENT_ID:CLIENT_IDis your SimpleLogin AppID. In OAuth terminology,clientis 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 whitelistslocalhostso you don't need to do anything for local development. Please see [App]({{< relref "/docs/app.md" >}}) for more information.scope=profile: SimpleLogin supports onlyprofilescope which contains user email, name and avatar.state=STATE: thisSTATEvalue 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 thesessionStoragethat 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.
REDIRECT_URI
#state=STATE
&scope=profile
&access_token=ACCESS_TOKENLet's break the components down:
REDIRECT_URIthe sameREDIRECT_URIin the previous stepSTATE: the state generated in the previous step. It's your app's responsibility to compare thisSTATEto the one generated in previous step, typically by retrieving it from thesessionStorage.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 theaccess tokenhitting your back-end (if there's any) in theimplicit flow.
Here's the code snippet using fetch to get user info with the obtained access token:
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:
{
"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
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>Popup or not popup
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.htmlto speed up the load time:
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:
window.opener.postMessage(
{ auth: { token: access_token } },
window.opener.location
);On your app, you need to setup a event listener that handle this message
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)
npm install passport passport-openidconnect express-session --saveThen please store your SimpleLogin AppID and AppSecret somewhere, preferably in env variable as recommended in the The Twelve Factors.
export CLIENT_ID={your_app_id}
export CLIENT_SECRET={your_app_secret}Bootstrap the app:
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:
// 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.
// 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:
// 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:
app.listen(3000, () => console.log(`App listening`))And run the app
node app.jsNow you should be able to sign in with SimpleLogin at http://localhost:3000/login