Can chrome.identity.launchWebAuthFlow be used to authenticate against Google APIs? - authentication

I'm writing a Chrome extension and have been trying to use chrome.identity.launchWebAuthFlow to authenticate with Google. I would prefer this to chrome.identity.getAuthToken (which does work) because getAuthToken gets the token for the user currently logged in to Chrome -- who may be logged in to multiple Google accounts. I want the user to be able to hook up a specific Google calendar to my extension, and that calendar might belong to a different user than they've logged in to Chrome as.
So, I've been trying to do this with chrome.identity.launchWebAuthFlow and generally failing around a mismatched redirect_uri. I've tried just about every type of credential you can set up in the Google APIs developer console. ("Chrome App" seemed like the right thing, but I have also tried Web application, Other, and iOS.) I've tried using the results of both chrome.extension.getURL('string') and chrome.app.getRedirectURL('string') as my redirect_uri.
I tried out the example app referred to by https://stackoverflow.com/questions/40384255/oauth2-angular-chrome-extension but have not been able to get that to work either.
I have a suspicion I'm trying to do something that either used to be allowed and no longer is, or just never worked.
Here's an example of my code, but I think my problem is really in the API dev console -- I don't see a way to set up a configuration that will work for an extension:
var auth_url = 'https://accounts.google.com/o/oauth2/v2/auth';
var client_key = *[client id from API dev console]*
var auth_params = {
client_id: client_key,
redirect_uri: chrome.identity.getRedirectURL("oauth2.html")
scope: 'https://www.googleapis.com/auth/calendar'
};
auth_url += '?' + $.param(auth_params);
chrome.identity.launchWebAuthFlow({url: auth_url, interactive: true}, function(token) { console.log(token); });
(I have also tried the https://accounts.google.com/o/oauth2/auth endpoint.)
Solution:
After reading the accepted answer, I wound up with this:
var auth_url = 'https://accounts.google.com/o/oauth2/auth';
var client_id = '[client ID from console]';
var redirect_url = chrome.identity.getRedirectURL("oauth2.html");
var auth_params = {
client_id: client_id,
redirect_uri: redirect_url,
response_type: 'token',
scope: 'profile'
};
auth_url += '?' + $.param(auth_params);
console.log(auth_url);
chrome.identity.launchWebAuthFlow({url: auth_url, interactive: true}, function(responseUrl) { console.log(responseUrl); });
The responseUrl is my redirect_uri with parameters -- so Google oauth returned that instead of redirecting the browser to it -- and I could go on from there.

Yes, in 2019 it still works. Finally got it working...
manifest.json
{
"name": "Extension Name",
"description": "Description",
"version": "1.0.0",
"manifest_version": 2,
"icons": {
"48": "icons/icon_48.png",
"128": "icons/icon_128.png"
},
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"oauth2": {
"client_id": "Your Client ID from Google Develpers console (Must be Web Application)",
"scopes": [
"openid", "email", "profile"
]
},
"permissions": [
"identity"
],
"key": "Your Key from Google Developer Dashboard"
}
background.js
chrome.windows.create({
'url': './content/auth/auth.html',
'width': 454,
'height': 540,
'type': 'popup'
});
auth.html
standard HTML markup that calls auth.js file
auth.js
var auth_url = 'https://accounts.google.com/o/oauth2/auth?';
var client_id = '<Client ID>'; // must be Web Application type
var redirect_url = chrome.identity.getRedirectURL(); // make sure to define Authorised redirect URIs in the Google Console such as https://<-your-extension-ID->.chromiumapp.org/
var auth_params = {
client_id: client_id,
redirect_uri: redirect_url,
response_type: 'token',
scope: 'https://mail.google.com/',
login_hint: 'real_email#gmail.com' // fake or non-existent won't work
};
const url = new URLSearchParams(Object.entries(auth_params));
url.toString();
auth_url += url;
chrome.identity.launchWebAuthFlow({url: auth_url, interactive: true}, function(responseUrl) {
console.log(responseUrl);
});

To get the Angular sample running, I needed to:
Create my own Web Application client ID in the Google developer console with an Authorized redirect URI of https://bcgajjfnjjgadphgiodlifoaclnemcbk.chromiumapp.org/oauth2
Copy that client ID into the config.json file of the sample.
The call to get redirectURI in that sample is like chrome.identity.getRedirectURL("oauth2"), the string parameter gets appended to the end of the URL based on extension ID.

Related

Office 365 Oauth with Nodemailer Can't create new access token

I am trying to use Nodemailer in express server with Oauth from Office 365 but I am getting Can't create new access token for user and {"code": "EAUTH", "command": "AUTH XOAUTH2" error. It seems like nodemailer is unable to obtain either the access token and refresh token and the user is not being authenticated to send mails.
const transporter = nodemailer.createTransport({
host: "smtp.office365.com",
port: 587,
secure: false,
tls: {
ciphers: "SSLv3"
},
requireTLS: true,
auth: {
type: "OAuth2",
user: process.env.SENDER_EMAIL,
clientId: "CLIENT_ID",
clientSecret: "CLEINT_SECRET",
accessUrl: "https://login.microsoftonline.com/SOMETHING_SECRET_HERE/oauth2/v2.0/authorize"
// pass: process.env.SENDER_PASSWORD
}
});
I am not familiar with OAuth 2.0 with Office 365 to begin with so there could be some configurations error etc. The nodemailer works fine if I use my account credentials though. Can someone please suggest me something to try out or let me know if my config is wrong.
Please note that, accessUrl is an endpoint for requesting new access token.
As you have passed Authorization endpoint, please try changing it to OAuth 2.0 token endpoint (v2).
You can find this endpoint value in the Portal like below:
Go to Azure Portal -> Azure Active Directory -> App Registrations -> Your App
Alternatively, you can make use of Microsoft Graph API to send the mails like below:
Make sure to have Mail.Send permission consented before using the below query:
POST https://graph.microsoft.com/v1.0/me/sendMail
{
"message": {
"subject": "Regarding leave approval",
"body": {
"contentType": "Text",
"content": "Please approve my leave request."
},
"toRecipients": [
{
"emailAddress": {
"address": "XXXXX"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"address": "XXXXX"
}
}
]
},
"saveToSentItems": "false"
}
Response:
The mail was successfully triggered like below:
For more in detail, please refer the below links:
Send mail - Microsoft Graph v1.0 | Microsoft Docs
Modern Oauth2 authentication for sending mails using Nodemailer nodejs by Sivaprakash-MSFT

PassportJS OAuth2Strategy: authenticate returns 400 instead of redirecting

I'm trying to setup discord oauth2 pkce using passportjs and the passport-oauth2
const discordStrategy = new OAuth2Strategy({
authorizationURL: 'https://discord.com/api/oauth2/authorize',
tokenURL: 'https://discord.com/api/oauth2/token',
clientID: DISCORD_CLIENT_ID,
clientSecret: DISCORD_CLIENT_SECRET,
callbackURL: DISCORD_CALLBACK_URL,
state: true,
pkce: true,
scope: ['identity', 'scope'],
passReqToCallback: true,
},
(req: Request, accessToken: string, refreshToken: string, profile: DiscordUserProfile, cb: any) => {
prisma.user.findUnique({ where: { email: profile.email ?? '' }}).then(foundUser => {
if (foundUser === null) {
// Create a new user with oauth identity.
} else {
cb(null, foundUser)
}
}).catch(error => {
cb(error, null);
})
});
I've been following the google example as well as some others, these examples indicate that, I should be able to use:
passport.use('discord', discordStrategy);
and
authRouter.get('/discord', passport.authenticate('discord'));
and this should redirect to the OAuth2 providers login page, but instead, I get a 400 Bad Request "The request cannot be fulfilled due to bad syntax." The response body contains an object:
{"scope": ["0"]}
Why is this happening instead of the expected redirect?
My intention is that, once the user logs in, I should get a code, then I can post that code and the code verifier to get an access token, then once the access token is obtained, the actual authenticate call can be made
Edit: I put breakpoints in the passport.authenticate function and I stepped through it. It does actually get through everything and it calls the redirect. The parsed URL it generates, even if I copy it and manually navigate to the URL, it gives me the same, just gives:
{"scope": ["0"]}
and no login page, why?
If you add a version number to the base api url, e.g. /v9 it gives a full error message.
It turned out I had typo'd the scopes, I had 'identity' instead of 'identify' - now this part of the process is working as expected.

BigCommerce StoreFront API SSO - Invalid login. Please attempt to log in again

Been at this for a few days. I am making a login form on my angular/nodejs app. The bc-api is able to verify the user/password. Now with that i need to allow the customer to enter the store with sso but the generated jwt is not working. My attempt below... I am looking for troubleshooting tips.
Generate JWT / sso_url
var jwt = require('jwt-simple');
function decode_utf8(s) {
return decodeURIComponent(escape(s));
}
function get_token(req, data) {
let uid = req.id;
let time = Math.round((new Date()).getTime() / 1000);
let payload = {
"iss": app.clientId,
// "iat": Math.floor(new Date() / 1000),
"iat": time,
"jti": uid+"-"+time,
"operation": "customer_login",
"store_hash": app.storeHash,
"customer_id": uid,
"redirect_to": app.entry_url
}
let token = jwt.encode(payload, app.secret, 'HS512');
token = decode_utf8(token);
let sso_url = {sso_url: `${app.entry_url}/login/token/${token}`}
return sso_url
}
payload resolves to
{
"iss": "hm6ntr11uikz****l3j2o662eurac9w",
"iat": 1529512418,
"jti": "1-1529512418",
"operation": "customer_login",
"store_hash": "2bihpr2wvz",
"customer_id": "1",
"redirect_to": "https://store-2bihpr2wvz.mybigcommerce.com"
}
generated sso_url
https://store-2bihpr2wvz.mybigcommerce.com/login/token/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJobTZudHIxMXVpa3oxMXpkbDNqMm82NjJldXJhYzl3IiwiaWF0IjoxNTI5NTEyNDE4LCJqdGkiOiIxLTE1Mjk1MTI0MTgiLCJvcGVyYXRpb24iOiJjdXN0b21lcl9sb2dpbiIsInN0b3JlX2hhc2giOiIyYmlocHIyd3Z6IiwiY3VzdG9tZXJfaWQiOiIxIiwicmVkaXJlY3RfdG8iOiJodHRwczovL3N0b3JlLTJiaWhwcjJ3dnoubXliaWdjb21tZXJjZS5jb20ifQ.vaeVTw4NjvX6AAPChgdXgMhm9b1W5B2QEwi4sJ6jz9KsKalqTqleijjRKs8jZP8jdQxC4ofYX5W0wYPMTquxQQ
result
about my env
I am using nodejs express... my bc app's secret & clientId are being used above and they work for several other bc-api tasks. My app is installed and authenticated on bc admin. The app being used to do the above is running on localhost but i also tried online https (same result).
I am thinking that there might be some incorrect configuration in my stores admin but havent found anything to change.
I decoded your JWT on jwt.io and I get this:
Header:
{
"typ": "JWT",
"alg": "HS512"
}
There's at least one problem here
BC requires HS256 as the algorithm according to docs
https://developer.bigcommerce.com/api/v3/storefront.html#/introduction/customer-login-api
Body:
{
"iss": "hm6ntr11uikz11zdl3j2o662eurac9w",
"iat": 1529512418,
"jti": "1-1529512418",
"operation": "customer_login",
"store_hash": "2bihpr2wvz",
"customer_id": "1",
"redirect_to": "https://store-2bihpr2wvz.mybigcommerce.com"
}
Problems here:
JTI should be a totally random string, using something containing the time could result in duplicates which will be rejected. Try using a UUID
Customer ID should be an int, not a string
The redirect_to parameter accepts relative URLs only. So try "redirect_to": "/" if your goal is to redirect to the home page.
Another potential problem is system time - if your JWT was created in the "future" according to BC's server time, your JWT also won't work. You can use the /v2/time endpoint response to specify the IAT, or to keep your own clock in sync.

In auth0 lock, how to refresh the id_token?

I am building a cordova mobile app and trying to use the auth0 lock API. I am having trouble with the refresh token. I can retreive the refresh token in the authResult but cannot figure out how to actually refresh the id_token ( I suppose i could write the REST calsl myself )
In the v9 docs, it seems there used to be a method: https://auth0.com/docs/libraries/lock/v9/using-a-refresh-token
lock.getClient().refreshToken(refresh_token, function (err, delegationResult) {
// Get here the new JWT via delegationResult.id_token
});
However in lock v10 it seems this method doesn't exist any more: https://auth0.com/docs/libraries/lock/v10/api
Can anyone advise if there is a way to refresh the token using the lock API?
First, you need to either have included Auth0's script tag in your HTML:
<script src="https://cdn.auth0.com/js/lock/10.8/lock.min.js"></script>
Or, if you've installed from npm, you can require Auth0:
var Auth0 = require("auth0-js");
In V10, you create an instance of the Auth0 client (separate from the Auth0Lock instance) which has a function refreshToken():
var auth0 = new Auth0({clientID: YOUR_CLIENT_ID, domain: YOUR_AUTH0_DOMAIN});
...
auth0.refreshToken(refresh_token_here, (err, resp) => {
// resp: {expires_in: 36000, id_token: "id_token here", token_type: "Bearer"}
}
The same can also be achieved by using the getDelegationToken() function:
auth0.getDelegationToken({
client_id: YOUR_CLIENT_ID,
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
refresh_token: refresh_token_here,
scope: "openid",
api_type: "auth0"
}, (err, resp) => {
// resp: {expires_in: 36000, id_token: "id_token here", token_type: "Bearer"}
});

Retrieve Google+ activity list of an user

I need to get the list of activities of an user in Google+. My coding platform is node.js Express framework and I'm using google-api-nodejs-client package.
var googleapis = require('googleapis');
var auth = new googleapis.OAuth2Client();
var accessToken="XXXXXX......";
googleapis
.discover('plus', 'v1')
.execute(function (err, client) {
if (err) {
console.log('Problem during the client discovery.', err);
return;
}
auth.setCredentials({
access_token: accessToken
});
client
.plus.people.get({ userId: 'me' })
.withAuthClient(auth)
.execute(function (err, response) {
console.log('My profile details------>', response);
});
client
.plus.activities.list({
userId: 'me',
collection: 'public',
maxResults: 100
})
.withAuthClient(auth)
.execute(function (err, response) {
console.log('err----------------------------->',err);//EDIT
console.log('activities---------------------->', response.items);
});
});
I got my profile details. But the activity is returning value: null. I checked my Google+ page to make sure that I have public posts. Also, I shared some posts to 'public' myself. Please help me find the bug in my code.
EDIT
Actually, there is an error. I found it by logging the value of err object in console as advised by Ryan Seys.
err--------------->
{
"error": {
"errors": [
{
"domain": "global",
"reason": "insufficientPermissions",
"message": "Insufficient Permission"
}
],
"code": 403,
"message": "Insufficient Permission"
}
}
It would help if you provide the value of the err object but here's a few thoughts:
Do you have Google+ API turned on for your project? See https://console.developers.google.com/ and the APIs and auth section of your project to enable the API.
Are you requesting the right scopes for user profile data. See https://developers.google.com/apis-explorer/#p/plus/v1/plus.activities.list to try out the request. Click the OAuth button on that page to see the different types of scopes you may like to try requesting from the user. Some of the scopes I see right now are:
https://www.googleapis.com/auth/plus.login (Know your basic profile info and list of people in your circles).
https://www.googleapis.com/auth/plus.me (Know who you are on Google)
https://www.googleapis.com/auth/userinfo.email (View your email address)
https://www.googleapis.com/auth/userinfo.profile (View basic information about your account)
Try adding an empty body field to the API request. This is a caveat of the current API client and some requests require you to enter a default empty {} after the parameter object.
client
.plus.activities.list({
userId: 'me',
collection: 'public',
maxResults: 100
}, {}) // <--- see the extra {} here!
.withAuthClient(auth)
.execute(function (err, response) {
console.log('activities---------------------->', response.items);
});
I think the problem is that you are specifying an empty fields parameter to client.plus.activities.list() instead of not providing a fields parameter at all. This tells it to return no fields for the results. Since the fields parameter is optional, you can omit it completely.
Try something like:
client
.plus.activities.list({
userId: 'me',
collection: 'public',
maxResults: 100
})