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);
I stitched together a lot of tutorials and documentation in order to get an access token with MSALin my JavaScript code. Here are the results of my research.
npm install #azure/msal
import the necessary class from #azure/msal
import {
UserAgentApplication,
AuthenticationParameters,
Configuration,
} from "#azure/msal";
Make the msal object
const config: Configuration = {
auth: {
clientId: <client id - your app's client id>,
authority: `https://login.microsoftonline.com/<tenantid>`,
redirectUri: <the redirect Uri>,
},
};
const params: AuthenticationParameters = {
authority: `https://login.microsoftonline.com/${Tenantid}`,
scopes: [`${AppIDUri}/user_impersonation`], <-- the API that you're trying to call
};
const myMSAL = new UserAgentApplication(config);
Get access token
try {
const login = await myMSAL.acquireTokenSilent(params);
return login.accessToken;
} catch (error) {
await myMSAL.loginPopup(params);
const login = await myMSAL.acquireTokenSilent(params);
return login.accessToken;
}
References:
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-acquire-cache-tokens
Azure/Msal authentication inside PowerApp Component Framework returns AADSTS50177 error
I'm trying to send an email through the gmail API from a Node.js application. I had this working, following the documentation and using the node-mailer package. However, I noticed that when we change our organizations password, the connection is no longer good (which makes sense). I'm therefore trying to authorize with a JWT instead.
The JWT is correctly generated and posted to https://oauth2.googleapis.com/token. This request then returns an access_token.
When it comes time to write and send the email, I tried to simply adapt the code that was previously working (at the time with a client_secret, client_id and redirect_uris):
const gmail = google.gmail({ version: 'v1', auth: access_token });
gmail.users.messages.send(
{
userId: 'email',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
The API keeps returning Error: Login Required.
Does anyone know how to solve this?
EDIT
I've modified my code and autehntication to add the client_id and client_secret:
const oAuth2Client = new google.auth.OAuth2(
credentials.gmail.client_id,
credentials.gmail.client_secret,
credentials.gmail.redirect_uris[0]
);
oAuth2Client.credentials = {
access_token: access_token
};
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
gmail.users.messages.send(
{
userId: 'email',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
But now the error is even less precise: Error: Bad Request
Here's the final authorization code that worked for me:
var credentials = require('../../credentials');
const privKey = credentials.gmail.priv_key.private_key;
var jwtParams = {
iss: credentials.gmail.priv_key.client_email,
scope: 'https://www.googleapis.com/auth/gmail.send',
aud: 'https://oauth2.googleapis.com/token',
exp: Math.floor(new Date().getTime() / 1000 + 120),
iat: Math.floor(new Date().getTime() / 1000),
sub: [INSERT EMAIL THAT WILL BE SENDING (not the service email, the one that has granted delegated access to the service account)]
};
var gmail_token = jwt.sign(jwtParams, privKey, {
algorithm: 'RS256'
});
var params = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: gmail_token
};
var params_string = querystring.stringify(params);
axios({
method: 'post',
url: 'https://oauth2.googleapis.com/token',
data: params_string,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(response => {
let mail = new mailComposer({
to: [ARRAY OF RECIPIENTS],
text: [MESSAGE CONTENT],
subject: subject,
textEncoding: 'base64'
});
mail.compile().build((err, msg) => {
if (err) {
return console.log('Error compiling mail: ' + err);
}
const encodedMessage = Buffer.from(msg)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
sendMail(encodedMessage, response.data.access_token, credentials);
});
});
So that code segment above uses a private key to create a JSON Web Token (JWT), where: iss is the service account to be used, scope is the endpoint of the gmail API being accessed (this must be preauthorized), aud is the google API oAuth2 endpoint, exp is the expiration time, iat is the time created and sub is the email the service account is acting for.
The token is then signed and a POST request is made to the Google oAuth2 endpoint. On success, I use the mailComposer component of NodeMailer to build the email, with an array of recipients, a message, a subject and an encoding. That message is then encoded.
And here's my sendMail() function:
const oAuth2Client = new google.auth.OAuth2(
credentials.gmail.client_id,
credentials.gmail.client_secret,
credentials.gmail.redirect_uris[0]
);
oAuth2Client.credentials = {
access_token: access_token
};
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
gmail.users.messages.send(
{
userId: 'me',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
In this function, I am creating a new googleapis OAuth2 object using the credentials of the service account (here stored in an external file for added security). I then pass in the access_token (generated in the auth script with the JWT). The message is then sent.
Pay attention to the userId: 'me' in the sendMail() function, this was critical for me.
This is the way I was able to only use googleapis package instead of axios + googleapis with your service account. You will need domain wide authority for this account with the scope used below associated with it. Follow this to do that https://support.google.com/a/answer/162106?hl=en
You can also use the mailComposer example up above to create the email. keys is the service_credentials.json file you get when making this service account
const { google } = require('googleapis');
const scope = ["https://www.googleapis.com/auth/gmail.send"];
const client = new google.auth.JWT({
email: keys.client_email,
key: keys.private_key,
scopes: scope,
subject: "emailToSendFrom#something.com",
});
await client.authorize();
const gmail = google.gmail({ version: 'v1', auth: client});
const subject = 'π€ Hello π€';
const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
const messageParts = [
'From: Someone <emailToSendFrom#something.com>',//same email as above
'To: Someone <whoever#whoever.com>',
'Content-Type: text/html; charset=utf-8',
'MIME-Version: 1.0',
`Subject: ${utf8Subject}`,
'',
'This is a message just to say hello.',
'So... <b>Hello!</b> π€β€οΈπ',
];
const message = messageParts.join('\n');
// The body needs to be base64url encoded.
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
const res = await gmail.users.messages.send({
userId: 'me',
requestBody: {
raw: encodedMessage,
},
});
console.log(res.data);
I'm to new GraphQL and I'm wondering how I can stay logged in when I want to do mutations as a logged in user in the GraphQL playground. How would I stayed logged in?
const getLoggedInUser = async req => {
const token = req.headers["x-token"];
if (token) {
try {
return await jwt.verify(token, process.env.JWT_SECRET);
} catch (e) {
throw new AuthenticationError(AUTHORISATION_MESSAGES.SESSION_EXPIRED);
}
}
return token;
};
Base on your code, you should pass the token in HTTP header like this
{
βx-tokenβ: β<put ur token here>β
}
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.