Login with Google in React Access Token problem - google-api

I have employed the Login with Google functionality in my React app. I am getting the jwt but there is no access token included in the jwt which I need for sending it to the backend (Laravel). On the backend I use Socialite and I want to get the user back with the access token. Right now I am verifying the user with jwt which is not working.
React Code.
const handleGoogleCallbackResponse = (response) => {
signinWithGoogle(response.credential)
}
const signinWithGoogle = async (jwt) => {
try {
const res = await axios.post("/api/users/loginwithgoogle", {jwt: jwt})
console.log("Google data from backend: ", res.data);
} catch (error) {
console.log("Error at signinWithGoogle : ", error);
}
}
useEffect(() => {
/* global google */
google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: handleGoogleCallbackResponse
})
google.accounts.id.renderButton(document.getElementById("google-btn"), {theme: "outline", size: "large"})
}, [])
Backend:
$user = Socialite::driver('google')->stateless()->userFromToken($request->jwt);

Related

How to get an access_token from googleapis?

I am trying to get an access token for accessing the Firebase Hosting API from a Service account, as described here.
The code below does not return an access_token, but an id_token instead, which fails to authenticate when trying to use the API.
What am I doing wrong? How can I obtain an access token?
const { google } = require("googleapis");
var serviceAccount = require("../functions/src/services/serviceAccountKey.json");
async function getAccessToken() {
try {
const jwtClient = new google.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
["firebasehosting.googleapis.com"],
null
);
const credentials = await jwtClient.authorize();
console.log(credentials);
} catch (error) {
console.log(error);
}
}
getAccessToken();
It returns a credentials object:
{
access_token: undefined,
token_type: 'Bearer',
expiry_date: undefined,
id_token: '...', // edited out
refresh_token: 'jwt-placeholder'
}
For the record, I finally got it.
My token scope was invalid: I should use https://www.googleapis.com/auth/firebase
The valid scopes are listed here

Social authentication in GraphQL

I am creating a backend which relies in Express and GraphQL which will serve clients apps (android and react).
I have been following this article on how to nail social authentication in GraphQL using passport.js.
The article uses passport-google-token strategy and is based on Apollo-server but personally I prefer to use express-graphql.
After setting my android app to use google auth and try to send mutation to server I get this error
Google info: { InternalOAuthError: failed to fetch user profile
at E:\_Projects\myProject\myProject-backend\node_modules\passport-google-token\lib\passport-google-token\strategy.js:114:28
at passBackControl (E:\_Projects\myProject\myProject-backend\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (E:\_Projects\myProject\myProject-backend\node_modules\oauth\lib\oauth2.js:157:7)
at IncomingMessage.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1125:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 401,
data:
'{\n "error": {\n "code": 401,\n "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",\n "status": "UNAUTHENTICATED"\n }\n}\n' } }
I believe token I pass maybe does not reach where it supposed but I cant figure out how to solve.
I have tested my token here https://oauth2.googleapis.com/tokeninfo?id_token=MyToken and they are working correctly.
Here is graphQL config in app.js
app.use('/graphql', graphqlHTTP((req, res) => ({
schema,
graphiql: true,
context: {req, res}
})));
Here is google auth mutation
googleAuth: {
type: authPayLoad,
args: {token: {type: new GraphQLNonNull(GraphQLString)}},
async resolve(_, {token}, {req, res}) {
req.body = {
...req.body,
access_token: token,
};
try {
const {data, info} = await authenticateGoogle(req, res);
console.log("Google data: ", data);
if (data) {
const user = await User.upsertGoogleUser(data);
if (user) {
return ({
name: user.name,
username: user.username,
token: user.generateJWT(),
});
}
}
if (info) {
console.log("Google info: ", info);
switch (info.code) {
case 'ETIMEDOUT':
return (new Error('Failed to reach Google: Try Again'));
default:
return (new Error('something went wrong'));
}
}
return (Error('server error'));
} catch (e) {
console.log("Error: ", e);
return e
}
},
},
And here is my auth controller
const passport = require('passport');
const {Strategy: GoogleTokenStrategy} = require('passport-google-token');
// GOOGLE STRATEGY
const GoogleTokenStrategyCallback = (accessToken, refreshToken, profile, done) => done(null, {
accessToken,
refreshToken,
profile,
});
passport.use(new GoogleTokenStrategy({
clientID: 'MY_CLIET_ID',
clientSecret: 'SERVER-SECRET'
}, GoogleTokenStrategyCallback));
module.exports.authenticateGoogle = (req, res) => new Promise((resolve, reject) => {
passport.authenticate('google-token', {session: false}, (err, data, info) => {
if (err) reject(err);
resolve({data, info});
})(req, res);
});
I expected when client app submit mutation with token as arg the request will be sent to google and returns user data. How do I solve this.
passport-google-token is archived and seems deprecated to me. Why don't you try passport-token-google.
It can be used in similar way.
passport.use(new GoogleStrategy({
clientID: keys.google.oauthClientID,
clientSecret: keys.google.oauthClientSecret
},
function (accessToken, refreshToken, profile, done) {
return done(null, {
accessToken,
refreshToken,
profile,
});
}
));
module.exports.authenticate = (req, res) => new Promise((resolve, reject) => {
passport.authenticate('google-token', {session: false}, (err, data, info) => {
if (err) reject(err);
resolve({data, info});
})(req, res);
});
Hope this helps.

Authenticating my Ionic 3 app against Spring Boot REST API

The question must be very typical, but I can't really find a good comparison.
I'm new to Ionic & mobile dev.
We have a REST API (Spring Boot).
API is currently used by AngularJS 1.5 front-end only.
AngularJS app is authenticated based on the standard session-based authentication.
What should I use to authenticate an ionic 3 app?
As I understand, have 2 options:
Use the same auth as for Angular front-end.
implement oauth2 on the back-end and use the token for the ionic app.
As for now, I understand that implementing oauth2 at back-end is a way to go because with the option #1 I should store the username & password in the local storage (ionic app), which is not safe. Otherwise, if I don't do that - the user will have to authenticate each time the app was launched. Am I right?
So, that leaves me with option #2 - store oauth2 token on the device?
Good to go with #2. Here is how i manage token.
I use ionic storage to store token and a provider config.ts which hold the token during run time.
config.ts
import { Injectable } from '#angular/core';
#Injectable()
export class TokenProvider {
public token: any;
public user: any = {};
constructor( ) { }
setAuthData (data) {
this.token = data.token;
this.user = data
}
dropAuthData () {
this.token = null;
this.user = null;
}
}
auth.ts
import { TokenProvider} from '../../providers/config';
constructor(public tokenProvider: TokenProvider) { }
login() {
this.api.authUser(this.login).subscribe(data => {
this.shared.Loader.hide();
this.shared.LS.set('user', data);
this.tokenProvider.setAuthData(data);
this.navCtrl.setRoot(TabsPage);
}, err => {
console.log(err);
this.submitted = false;
this.shared.Loader.hide();
this.shared.Toast.show('Invalid Username or Password');
this.login.password = null;
});
}
and i do a check when app launch.
app.component.ts (in constructor)
shared.LS.get('user').then((data: any) => {
if (!data) {
this.rootPage = AuthPage;
} else {
tokenProvider.setAuthData(data);
this.rootPage = TabsPage;
}
});
api.provider.ts
updateUser(data): Observable < any > {
let headers = new Headers({
'Content-Type': 'application/json',
'X-AUTH-TOKEN': (this.tokenProvider.token)
});
return this.http.post(`${baseUrl}/updateUser`, JSON.stringify(data), {
headers: headers
})
.map((response: Response) => {
return response.json();
})
.catch(this.handleError);
}
And last logout.ts
logOut(): void {
this.shared.Alert.confirm('Do you want to logout?').then((data) => {
this.shared.LS.remove('user').then(() => {
this.tokenProvider.dropAuthData();
this.app.getRootNav().setRoot(AuthPage);
}, () => {
this.shared.Toast.show('Oops! something went wrong.');
});
}, err => {
console.log(err);
})
}
The final solution i've made:
ionic app:
implemented a jwt token storage similar to Swapnil Patwa answer.
Spring back-end:
Tried to use their original ouath2 package, but found out that as always with spring/java, configs are too time-consuming => made a simple filter which is checking for the manually generated & assigned jwt token.

Stormpath secure rest api

I followed the example here https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security
and here to acquire an access token
https://support.stormpath.com/hc/en-us/articles/225610107-How-to-Use-Stormpath-for-Token-Management
"use strict";
import { ApiKey } from 'stormpath';
import { Client } from 'stormpath';
let apiKey = new ApiKey(process.env.STORMPATH_API_KEY_ID,
process.env.STORMPATH_API_KEY_SECRET);
let spClient = new Client({apiKey: apiKey });
spClient.getApplication(process.env.STORMPATH_APPLICATION_HREF,
function(err, app) {
var authenticator = new OAuthAuthenticator(app);
authenticator.authenticate({
body: {
grant_type: 'password',
username: username,
password : password
}
}, function (err, result) {
if (!err) console.log(err);
res.json(result.accessTokenResponse);
});
});
I was able to acquire a access_token. I use this token to hit my api with Header Authorization Bearer {access_token}
However, when i put in the middleware stormpath.apiAuthenticationRequired, i keep getting this warning and my api is returned with 401
(node:57157) DeprecationWarning: JwtAuthenticator is deprecated, please use StormpathAccessTokenAuthenticator instead.

Parse server twitter authentication: Twitter auth integrated but unable to create session to use on client side

Parse Cloud code:
Parse.Cloud.define("twitter", function(req, res) {
/*
|--------------------------------------------------------------------------
| Login with Twitter
| Note: Make sure "Request email addresses from users" is enabled
| under Permissions tab in your Twitter app. (https://apps.twitter.com)
|--------------------------------------------------------------------------
*/
var requestTokenUrl = 'htt****/oauth/request_token';
var accessTokenUrl = 'http***itter.com/oauth/access_token';
var profileUrl = 'https://api.twitter.com/1.1/account/verify_credentials.json';
// Part 1 of 2: Initial request from Satellizer.
if (!req.params.oauth_token || !req.params.oauth_verifier) {
var requestTokenOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
oauth_callback: req.params.redirectUri
};
// Step 1. Obtain request token for the authorization popup.
request.post({
url: requestTokenUrl,
oauth: requestTokenOauth
}, function(err, response, body) {
var oauthToken = qs.parse(body);
// console.log(body);
// Step 2. Send OAuth token back to open the authorization screen.
console.log(oauthToken);
res.success(oauthToken);
});
} else {
// Part 2 of 2: Second request after Authorize app is clicked.
var accessTokenOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
token: req.params.oauth_token,
verifier: req.params.oauth_verifier
};
// Step 3. Exchange oauth token and oauth verifier for access token.
request.post({
url: accessTokenUrl,
oauth: accessTokenOauth
}, function(err, response, accessToken) {
accessToken = qs.parse(accessToken);
var profileOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
token: accessToken.oauth_token,
token_secret: accessToken.oauth_token_secret,
};
console.log(profileOauth);
// Step 4. Retrieve user's profile information and email address.
request.get({
url: profileUrl,
qs: {
include_email: true
},
oauth: profileOauth,
json: true
}, function(err, response, profile, USER) {
console.log(profile);
//console.log(response.email);
Parse.Cloud.useMasterKey();
var UserPrivateInfo = Parse.Object.extend("UserPrivateInfo");
var query = new Parse.Query(UserPrivateInfo);
query.equalTo("email", profile.email);
query.first({
success: function(privateInfo) {
if (privateInfo) {
res.success(privateInfo.get('user'));
} else {
response.success();
}
},
error: function(error) {
response.error("Error : " + error.code + " : " + error.message);
}
});
});
});
}
});
For client side using Sendgrid twitter authentication:
loginCtrl.twitterLogin = function() {
$auth.authenticate("twitter").then(function(response) {
console.log(response.data.result);
var user = response.data.result;
if (!user.existed()) {
var promise = authFactory.saveUserStreamDetails(user, response.email);
promise.then(function(response) {
signInSuccess(response);
}, function(error) {
console.log("error while saving user details.");
});
} else {
signInSuccess(user);
}
}).catch(function(error) {
console.log(error);
});;
};
Issue:
Step 1: Called cloud function Parse.Cloud.define("twitter", function(req, res) using loginCtrl.twitterLogin
Step 2: Twitter popup opens and user logs in to twitter
Step 3: Got verification keys and again cloud function Parse.Cloud.define("twitter", function(req, res) is called and user is verified
Step 4: Got the user email using the twitter API.
Step 5: I can get the existing Parse User Object using the email or can signUp using that email.
Step 6: Returns the parse user object to client but there is no session attached to it so **How can I create user session?
Without parse session we can not log in to parse application. Every clound code api/ function call will fail as no session is attached to them. So how can I create and manage a session using twitter authentication.

Resources