Auth0 LoopbackJS API access token using 3rd party login - express

I currently have the loopbackJS api hosted on a domain (e.g. http://backend.com), with third party authentication setup via Auth0. I have a front-end hosted as a SPA on another domain (e.g. http://frontend.com)
loopback-component-passport seems to work fine when the front-end is on the same domain as the API, and it sets the userId and access_token cookies accordingly. However, my front-end in production is on a different domain to the API, for example the API auth link would be something like:
"http://backend.com/auth/auth0?returnTo=" + encodeURIComponent("http://frontend.com")
The backend has used the same auth pattern as in the loopback-passport-example, where a providers.json file specifies the connection details for Auth0 (although I have also tried other social providers such as Facebook).
"auth0-login": {
"provider": "auth0",
"module": "passport-auth0",
"clientID": "AUTH0_CLIENT_ID",
"clientSecret": "AUTH0_CLIENT_SECRET",
"callbackURL": "/auth/auth0/callback",
"authPath": "/auth/auth0",
"callbackPath": "/auth/auth0/callback",
"successRedirect": "/",
"failureRedirect": "/login",
"scope": ["email"],
"failureFlash": true
}
The front-end (http://frontend.com) has a link on the page to redirect to the API authentication:
Login
Clicking on this link redirects to Auth0 properly, and I can login. It then redirects to the specified target (http://backend.com or http://frontend.com, whichever is specified). The returnTo query parameter also seems to work as expected.
Is there a way to capture the access_token just before redirecting back to the front-end, and somehow communicate it (e.g. query parameters, unless that would be too insecure).

After some more investigation, I settled on this method to use for passing the access token and userId from loopbackjs backend, to a separate front-end. This was documented on a github pull-request, using a customCallback of passport-configurator.
Other places that have referenced this are this fork, issue #102, issue #14 and pull request #155.
There are 2 options here, either use a fork of loopback-component-passport (e.g. the one referenced above) as your npm dependency, or provide a customCallback as a passport configuration option as documented.
I wanted a little more control on the format of the URL, so ended up with the customCallback method. In loopback-example-passport, inside /server/server.js there is some basic code for passing providers.json to the passport configurator:
var config = {};
try {
config = require('../providers.json');
} catch (err) {
console.trace(err);
process.exit(1); // fatal
}
passportConfigurator.init();
for (var s in config) {
var c = config[s];
c.session = c.session !== false;
passportConfigurator.configureProvider(s, c);
}
This can be essentially replaced with the documented customCallback code, with the passport variable being assigned by passportConfigurator.init():
var providers = {};
try {
providers = require('../providers.json');
} catch (err) {
console.trace(err);
process.exit(1); // fatal
}
const passport = passportConfigurator.init();
Object.keys(providers).forEach(function(strategy) {
var options = providers[strategy];
options.session = options.session !== false;
var successRedirect = function(req) {
if (!!req && req.session && req.session.returnTo) {
var returnTo = req.session.returnTo;
delete req.session.returnTo;
return returnTo;
}
return options.successRedirect || '';
};
options.customCallback = !options.redirectWithToken
? null
: function (req, res, next) {
var url = require('url');
passport.authenticate(
strategy,
{session: false},
function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.redirect(options.failureRedirect);
}
var redirect = url.parse(successRedirect(req), true);
delete redirect.search;
redirect.query = {
'access_token': info.accessToken.id,
'userId': user.id.toString()
};
redirect = url.format(redirect);
return res.redirect(redirect);
}
)(req, res, next);
};
passportConfigurator.configureProvider(strategy, options);
});
In the above example, I have essentially copied the successRedirect function used in passport-configurator.js, to use the same returnTo query parameter. An option within providers.json can be set e.g. "redirectWithToken": true, which results in redirect only for the auth strategies that need external redirect.
One more final bit of code in case the returnTo redirect is required. If it exists as a query parameter, it should be added at a session level:
app.use(function(req, res, next) {
var returnTo = req.query.returnTo;
if (returnTo) {
req.session = req.session || {};
req.session.returnTo = require('querystring').unescape(returnTo);
}
next();
});
Now, if the backend api is at a URL such as http://api.com, and the front-end is hosted at another domain e.g. http://gui.com, an authentication link can be placed on the front-end:
Login!
This will result in an API auth call, then redirect back to the returnTo link with the access token and userId in the query parameters.
Potentially in the future, one of the issues or other pull requests will be merged that could provide a more ideal method for 3rd party domain redirection, but until then this method work well.

Related

How to redirect from GraphQL middleware resolver on authentication fail?

Introduction:
I am using GraphQL Mesh as a gateway between my app and an API. I use Apollo Client as the GraphQL client. When a user wants to visit the first screen after hitting the log-in button, I do a query to load data from a CMS. This query has to go through the gateway. In the gateway I do an auth check to see if the user has a valid JTW access token, if not, I want to redirect back to the sign-in page. If the user has a token, he is let through.
The gateway is-auth.ts resolver:
const header = context.headers.authorization;
if (typeof header === "undefined") {
return new Error("Unauthorized: no access token found.");
} else {
const token = header.split(" ")[1];
if (token) {
try {
const user = jwt.verify(token, process.env.JWT_SECRET as string);
} catch (error) {
return new Error("Unauthorized: " + error);
}
} else {
return new Error("Unauthorized: no access token found.");
}
}
return next(root, args, context, info);
},
Problem: Right now, I am returning Errors in the authentication resolver of the gateway, hoping that I could pick them up in the error object that is sent to Apollo Client and then redirect off of that. Unfortunately, I don't get that option, since the Errors are thrown immediately, resulting in an error screen for the user (not what I want). I was hoping this would work in order to redirect to the sign-in from the client-side, but it does not work:
const { data, error } = await apolloClient(accessToken).query({
query: gql`
query {
...where my query is.
}
`,
});
if (error) {
return {
redirect: {
permanent: false,
destination: `/sign-in`,
},
};
}
Does anyone perhaps have a solution to this problem?
This is the GraphQL Mesh documentation on the auth resolver, for anyone that wants to see it: https://www.graphql-mesh.com/docs/transforms/resolvers-composition. Unfortunately, it doesn't say anything about redirects.
Kind regards.

auth0 checkSession({}) returns login_required when logged in through social provider, but not when logging in via username/password

I have an Angular app that uses Auth0 for authentication, and I'm trying to use checkSession({}, …) to persist a user's session if the token hasn't expired yet.
When I log in with my username/pw that I set up for the site, this works fine when I reload the browser/navigate directly to a resource. However, when I log in using a social provider (such as Google), the checkSession({}, …) call on a page reload returns an error and forces the user to log in again.
Some of the relevant code (mostly from the auth0 tutorial(s)):
export class AuthService {
// Create Auth0 web auth instance
private _auth0 = new auth0.WebAuth({
clientID: AUTH_CONFIG.CLIENT_ID,
domain: AUTH_CONFIG.CLIENT_DOMAIN,
responseType: 'token',
redirectUri: AUTH_CONFIG.REDIRECT,
audience: AUTH_CONFIG.AUDIENCE,
scope: AUTH_CONFIG.SCOPE
});
accessToken: string;
userProfile: any;
expiresAt: number;
// Create a stream of logged in status to communicate throughout app
loggedIn: boolean;
loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);
loggingIn: boolean;
isAdmin: boolean;
// Subscribe to token expiration stream
refreshSub: Subscription;
constructor(private router: Router) {
// If app auth token is not expired, request new token
if (JSON.parse(localStorage.getItem('expires_at')) > Date.now()) {
this.renewToken();
}
}
...
handleAuth() {
// When Auth0 hash parsed, get profile
this._auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken) {
window.location.hash = '';
this._getProfile(authResult);
} else if (err) {
this._clearRedirect();
this.router.navigate(['/']);
console.error(`Error authenticating: ${err.error}`);
}
this.router.navigate(['/']);
});
}
private _getProfile(authResult) {
this.loggingIn = true;
// Use access token to retrieve user's profile and set session
this._auth0.client.userInfo(authResult.accessToken, (err, profile) => {
if (profile) {
this._setSession(authResult, profile);
this._redirect();
} else if (err) {
console.warn(`Error retrieving profile: ${err.error}`);
}
});
}
private _setSession(authResult, profile?) {
this.expiresAt = (authResult.expiresIn * 1000) + Date.now();
// Store expiration in local storage to access in constructor
localStorage.setItem('expires_at', JSON.stringify(this.expiresAt));
this.accessToken = authResult.accessToken;
this.userProfile = profile;
if (profile) {
this.isAdmin = this._checkAdmin(profile);
}
...
}
...
get tokenValid(): boolean {
// Check if current time is past access token's expiration
return Date.now() < JSON.parse(localStorage.getItem('expires_at'));
}
renewToken() {
// Check for valid Auth0 session
this._auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken) {
this._getProfile(authResult);
} else {
this._clearExpiration();
}
});
}
}
(This is from a service that is called in many places within the app, including some route guards and within some components that rely on profile information. If more of the app code would be useful, I can provide it.)
Also note: AUTH_CONFIG.SCOPE = 'openid profile email'
So, the issue appears to not have been related to my app at all. When using Social Providers, Auth0 has an explicit note in one of their tutorials that really helped me out:
The issue with social providers is that they were incorrectly configured in my Auth0 dashboard, and needed to use provider-specific app keys.
Important Note: If you are using Auth0 social connections in your app,
please make sure that you have set the connections up to use your own
client app keys. If you're using Auth0 dev keys, token renewal will
always return login_required. Each social connection's details has a
link with explicit instructions on how to acquire your own key for the
particular IdP.
Comment was found on this page: https://auth0.com/blog/real-world-angular-series-part-7/

Grant Provider OAuth state mismatch when accessing the two application modules simultaneously on same browser and user is not logged in yet

I have been trying to implement Single SignOn(SSO). I have different frontend application modules which are running on different domain and they all utlize a single API server.
SSO Server https://sso.app.com
API Server https://api.app.com
Frontend Module 1 https://module-1.app.com
Frontend Module 2 https://module-2.app.com
Authentication flow
The flow of authentication is FrontEnd Module check for token in the localstorage. If it do not find the token, it redirect the user to API server endpoint let say https://api.app.com/oauth/connect.
API server has the clientId and Secrets for the SSO server. API server set the url of Frontend module in the cookie(so that i can redirect the user back to initiator frontend module) and then redirect the request to SSO server where user is presented with login screen. User enters the creds there, SSO server validate the credientials, creates a session.
Once credientials are validated, SSO server calls the API server Endpoint with user profile and access_token. API server gets the profile in the session and query and sign its own token and send that to frontend module through query params. On the frontEnd(React APP) there is a route just for this. In that frontend route I extract the token from queryParams and set in the localstorage. User is in the application.
Similarly when user loads the FrontendModule-2 same flow happend but this time because Session is being created by SSO server when FrontendModule-1 flow ran. it never ask for login creds and sign the user in to the system.
Failing Scenario:
The scenario is, assume there is user JHON who is not logged in yet and do not have session. Jhon hit the "Frontend Module 1" URL in the browser. Frontend module check the localStorage for the token, it do not find it there, then Frontend module redirect the user to API server route.
API server has the clientSecret and clientId which redirect the request to SSO server. There user will be presented with Login Screen.
Jhon sees the login screen and left it as it is. Now Jhon opens another tab in the same browser and enter the URL of "Frontend Module 2". Same flow happen as above and Jhon lands on login screen. Jhon left that screen as it is and moves back to the first tab where he has Frontend Module 1 session screen loaded up. He enter the creds and hit the login button. It give me error that session state has been changed.
This error actually makes sense, because session is a shared.
Expectation
How do I achieve this without the error. I want to redirect the user to the same Frontend Module which initiated the request.
Tools that I am Using
NodeJS
grant-express
express-session
Sample Implementation (API Server)
require('dotenv').config();
var express = require('express')
, session = require('express-session')
, morgan = require('morgan')
var Grant = require('grant-express')
, port = process.env.PORT || 3001
, oauthConsumer= process.env.OAUTH_CONSUMER || `http://localhost`
, oauthProvider = process.env.OAUTH_PROVIDER_URL || 'http://localhost'
, grant = new Grant({
defaults: {
protocol: 'https',
host: oauthConsumer,
transport: 'session',
state: true
},
myOAuth: {
key: process.env.CLIENT_ID || 'test',
secret: process.env.CLIENT_SECRET || 'secret',
redirect_uri: `${oauthConsumer}/connect/myOAuth/callback`,
authorize_url: `${oauthProvider}/oauth/authorize`,
access_url: `${oauthProvider}/oauth/token`,
oauth: 2,
scope: ['openid', 'profile'],
callback: '/done',
scope_delimiter: ' ',
dynamic: ['uiState'],
custom_params: { deviceId: 'abcd', appId: 'com.pud' }
}
})
var app = express()
app.use(morgan('dev'))
// REQUIRED: (any session store - see ./examples/express-session)
app.use(session({secret: 'grant'}))
// Setting the FrontEndModule URL in the Dynamic key of Grant.
app.use((req, res, next) => {
req.locals.grant = {
dynamic: {
uiState: req.query.uiState
}
}
next();
})
// mount grant
app.use(grant)
app.get('/done', (req, res) => {
if (req.session.grant.response.error) {
res.status(500).json(req.session.grant.response.error);
} else {
res.json(req.session.grant);
}
})
app.listen(port, () => {
console.log(`READY port ${port}`)
})
You have to redirect the user back to the originating app URL not the API server URL:
.use('/connect/:provider', (req, res, next) => {
res.locals.grant = {dynamic: {redirect_uri:
`http://${req.headers.host}/connect/${req.params.provider}/callback`
}}
next()
})
.use(grant(require('./config.json')))
Then you need to specify both:
https://foo1.bar.com/connect/google/callback
https://foo2.bar.com/connect/google/callback
as allowed redirect URIs of your OAuth app.
Lastly you have to route some of the app domain routes to your API server where Grant is handling the redirect URI.
Example
Configure your app with the following redirect URI https://foo1.bar.com/connect/google/callback
Navigate to https://foo1.bar.com/login in your browser app
The browser app redirects to your API https://api.bar.com/connect/google
Before redirecting the user to Google, the above code configures the redirect_uri based on the incoming Host header of the request to https://foo1.bar.com/connect/google/callback
The user logs into Google and is being redirected back to https://foo1.bar.com/connect/google/callback
That specific route have to be redirected back to your API https://api.bar.com/connect/google/callback
Repeat for https://foo2.bar.com
you have relay_state option while hitting SSO server, that is returned as it was sent to SSO server, just to keep track of application state before requesting SSO.
TO learn more about relay state: https://developer.okta.com/docs/concepts/saml/
And which SSO service are you using??
The way I solved this problem by removing the grant-express implementation and use the client-oauth2 package.
Here is my implementation.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
const session = require('express-session');
const { JWT } = require('jose');
const crypto = require('crypto');
const ClientOauth2 = require('client-oauth2');
var logger = require('morgan');
var oauthRouter = express.Router();
const clientOauth = new ClientOauth2({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.SECRET,
accessTokenUri: process.env.ACCESS_TOKEN_URI,
authorizationUri: process.env.AUTHORIZATION_URI,
redirectUri: process.env.REDIRECT_URI,
scopes: process.env.SCOPES
});
oauthRouter.get('/oauth', async function(req, res, next) {
try {
if (!req.session.user) {
// Generate random state
const state = crypto.randomBytes(16).toString('hex');
// Store state into session
const stateMap = req.session.stateMap || {};
stateMap[state] = req.query.uiState;
req.session.stateMap = stateMap;
const uri = clientOauth.code.getUri({ state });
res.redirect(uri);
} else {
res.redirect(req.query.uiState);
}
} catch (error) {
console.error(error);
res.end(error.message);
}
});
oauthRouter.get('/oauth/callback', async function(req, res, next) {
try {
// Make sure it is the callback from what we have initiated
// Get uiState from state
const state = req.query.state || '';
const stateMap = req.session.stateMap || {};
const uiState = stateMap[state];
if (!uiState) throw new Error('State is mismatch');
delete stateMap[state];
req.session.stateMap = stateMap;
const { client, data } = await clientOauth.code.getToken(req.originalUrl, { state });
const user = JWT.decode(data.id_token);
req.session.user = user;
res.redirect(uiState);
} catch (error) {
console.error(error);
res.end(error.message);
}
});
var app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
secret: 'My Super Secret',
saveUninitialized: false,
resave: true,
/**
* This is the most important thing to note here.
* My application has wild card domain.
* For Example: My server url is https://api.app.com
* My first Frontend module is mapped to https://module-1.app.com
* My Second Frontend module is mapped to https://module-2.app.com
* So my COOKIE_DOMAIN is app.com. which would make the cookie accessible to subdomain.
* And I can share the session.
* Setting the cookie to httpOnly would make sure that its not accessible by frontend apps and
* can only be used by server.
*/
cookie: { domain: process.env.COOKIE_DOMAIN, httpOnly: true }
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/connect', oauthRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
In my /connect/oauth endpoint, instead of overriding the state I create a hashmap stateMap and add that to session with the uiState as a value received in the url like this https://api.foo.bar.com?uiState=https://module-1.app.com
When in the callback I get the state back from my OAuth server and using the stateMap I get the uiState value.
Sample stateMap
req.session.stateMap = {
"12313213dasdasd13123123": "https://module-1.app.com",
"qweqweqe131313123123123": "https://module-2.app.com"
}

How should I handle nuxt cookies expiration and workflow?

I have made an authentication workflow for a project using a Nuxt frontend(universal mode) and an Apollo endpoint as backend.
It is a mix of several examples I found and, with SSR, and since I do not fully anticipate what could go wrong, I wanted to make sure there is no red flag about how I proceed.
On the backend, I use an express middleware to sign JWT auth tokens, check them, and return them in the Authorization header. Here is the middleware:
import jwt from 'jsonwebtoken';
import { AuthenticationError } from 'apollo-server-express';
export const getToken = payload => {
return jwt.sign(payload, process.env.SEED, { expiresIn: process.env.EXPTOKEN });
}
export const checkToken = (req, res, next) => {
const rawToken = req.headers["authorization"]
if (rawToken) {
try {
const token = rawToken.substring(7)
// Verify that the token is validated
const { user, role } = jwt.verify(token, process.env.SEED);
const newToken = getToken({ user, role });
req.user = user;
req.role = role;
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.set("Access-Control-Expose-Headers", "authorization");
res.set("authorization", newToken);
} catch (error) {
if (error.name === "TokenExpiredError") {
res.set("Access-Control-Expose-Headers", "authorization");
res.set("authorization", false);
}
console.log("invalid token", error);
return new AuthenticationError
// Invalid Token
}
}
next();
}
Since there is a Nuxt-Apollo module, I used its methods onLogin, onLogout and getToken to store the JWT string in a cookie. As I understand it, SSR apps don't have the serverside local storage matching the client so they have to use cookies. Correct?
Here is my nuxt middleware where I check the users credentials before allowing them to visit an auth route. Is is quite messy but it gets the job done, except for the commented part.
export default function ({ app, route, error, redirect }) {
const hasToken = !!app.$apolloHelpers.getToken()
// this part does not work
/* const tokenExpireDateTime = app.$cookies.nodeCookie.parse('cookie-name', 'expires')
if (hasToken && tokenExpireDateTime < 0) {
error({ statusCode: 403, message: 'Permission denied', description: 'Sorry, you are forbidden from accessing this page.' })
app.$apolloHelpers.onLogout()
return redirect('/login')
}
*/
if (!hasToken) {
if (route.name === 'welcome-key') {
// enrollment link route
} else {
if (route.name === 'home') {
error({ errorCode: 403, message: 'You are not allowed to see this' })
return redirect('/showcase')
}
if (!['login', 'forgot_password', 'reset_password-key'].includes(route.name)) {
error({ errorCode: 403, message: 'You are not allowed to see this' })
return redirect('/login')
}
}
} else {
if (['login', 'forgot_password', 'reset_password-key'].includes(route.name)) {
redirect('/')
}
}
}
I have one issue and several points of confusion.
My issue is that I can't get the cookie expires value to redirect in the above nuxt middlware if it is necessary to login again because the JWT is expired. I used the piece of code mentioned in this issue as reference.
With this issue, my confusion is about:
The expires date on the cookie is set by the Nuxt-Apollo module, I expect, and I have to make it match the duration set on server (i.e. process.env.EXPTOKEN in the server middleware mentioned above), correct?
That expiration time alone can easily be tempered with and the real security is the lack of a valid token in headers when a request is handled by my server middleware. Its use is for client-side detection and redirect of an expired token/cookie, and serverside prefetch of user related data during SSR. Right?
The new token emitted by my express backend middleware is not taken into account in my frontend: it is not updating the cookie stored JWT and expires value client side. I mean that I can see the autorization header JWT string being updated in the response, but the cookie isn't. The following request still use the first JWT string. Am I supposed to update it at each roundtrip? What am I missing with the approach of the express middleware (that, as you can guess, I didn't write)
Please help me understand better this workflow and how I could improve it. It tried to avoid as much as possible to make this question too broad, but if I can narrow it down more, feel free to suggest an edit.

Unable to Authorize users using Implicit / Authorization flow in google actions

I am trying to link to the account :
Here is my google cloud function
var AuthHandler = function() {
this.googleSignIn = googleSignIn;
this.googleSignInCallback = googleSignInCallback;
}
function googleSignIn(req, res, next) {
passport = req._passport.instance;
passport.authenticate('google',{scope: 'https://www.googleapis.com/auth/userinfo.email',
state:"google",response_type:"token"},
function(err, user, info) {
console.log(user);
})(req,res,next);
};
function googleSignInCallback(req, res, next) {
passport = req._passport.instance;
passport.authenticate('google',function(err, user, info) {
if(err) {
return next(err);
}
if(!user) {
return res.redirect('http://localhost:8000');
}
console.log(user._json.token);
// /res.redirect('/');
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx#access_token=' + user._json.token + '&token_type=bearer&state=google')
})(req,res,next);
};
module.exports = AuthHandler;
In google Action Console :
I have created the implicit flow and gave my authorisation url as follows:
https://[region]-[projectid].cloudfunctions.net/[functionname]/auth/google
Error :
this is the browser Url
https://assistant.google.com/services/auth/handoffs/auth/complete?state=xxxx&code=xxxxxx
on which the following error is displayed
The parameter "state" must be set in the query string.
Update 1
Before starting this implementation , i have followed this Solution to create the Authentication.
Problems in this Approach :
1.As stated in the Documentation it is not redirecting to google.com and i'm unable to access the token using the APIAI SDK in javascript. but still i can see the Access token in emulator . for better understanding adding images
Here is my simulator O/P
{
"response": {
"debug": {
"agentToAssistantDebug": {
"assistantToAgentDebug": {
"assistantToAgentJson": "{"accessToken\":\"xxxxxx\""
}
},
"errors": []
}
Update 2 :
So i have started creating with implicit flow and here is my complete repo
After battling with it i have achieved it , as there is no proper articles about creation of own Oauth Server that implements the Google Action , this might helpful for future users.
Authorization Endpoint
app.get('/authorise', function(req, res) {
req.headers.Authorization = 'Bearer xxxxxxxxxxx';
// with your own mechanism after successful
//login you need to create a access token for the generation of
//authorization code and append it to this header;
var request = new Request(req);
var response = new Response(res);
oauth.authorize(request, response).then(function(success) {
// https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?
//code=AUTHORIZATION_CODE&state=STATE_STRING
var toredirect = success.redirectUri +"?code="+success.code
+"&state="+request.query.state ;
return res.redirect(toredirect);
}).catch(function(err){
res.status(err.code || 500).json(err)
}) });
Token Endpoint :
app.all('/oauth/token', function(req,res,next){
var request = new Request(req);
var response = new Response(res);
oauth
.token(request,response)
.then(function(token) {
// Todo: remove unnecessary values in response
return res.json(token)
}).catch(function(err){
return res.status(500).json(err)
})
});
After creation of this endpoints publish to the Google Cloud functions . I have used MYSQL as the DB using SEQUELIZE and Oauth-Server , if anyone need those models , will share it through repo .
With this you can able to link account using your own Server which implements
Auth tokens and Access Tokens
I think the problem is that the URL on this line isn't sending the parameters as query parameters, they're sending them as part of the anchor:
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx#access_token=' + user._json.token + '&token_type=bearer&state=google')
You should replace the # with a ?, as illustrated here:
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx?access_token=' + user._json.token + '&token_type=bearer&state=google')