How to enable server side offline access for Chrome Extension OAuth? - google-chrome-extension

I am working on a Chrome extension where the backend server requires offline access and a refresh token for the user's account. I am not familiar with Chrome extensions, and not sure how I should do the authorization in this case.
The benefit of using chrome.identity is that I can set interactive=false and attempt to authorize without bothering the user when the plugin loads. However, the getAuthToken() method doesn't provide a way for the backend to get a refresh token.
If, instead, I use the launchWebAuthFlow() method and redirect user back to OAuth page, i.e.
chrome.identity.launchWebAuthFlow({
url: https://*<my_host>*/auth/google,
interactive: true,
}, function(url) {
var uri = URI(url),
params = uri.search(true);
var token = params.authToken;
done(token);
});
where https://*<my_host>*/auth/google is my server OAuth path, then for some reason the Permissions scope page doesn't show up. Instead, it shows the Google login page. (Note: if I navigate to https://*<my_host>*/auth/google using the browser, it permissions page shows correctly.)
Is there anyway to do this with chrome.identity API? If not, do I have to do the OAuth using a popup? What's the proper way to do this normally?

Related

Azure MSAL authentication - SSO with MFA

I have an an iframe, which uses MSAL authentication. it's parent also uses the same auth mechanism and once the user has logged in the parent app, I should be able to log him in the iframe with SSO. I tried doing it with loginHint but I get this error:
That's the piece of code in the iframe that receives the loginHint from the parent and trying to use it for SSO:
window.addEventListener("message", (event) => {
// check the origin of the data
if (event.origin === parentDomain) {
const loginHint = event.data;
// attempt SSO
publicClient
.ssoSilent({
scopes: ["openid", "profile", "User.Read"],
loginHint,
})
.then((res) => {
console.log(res);
})
.catch((error) => {
console.error(error);
});
}
});
I think it might have something to do with the fact that my organization is using MFA (Multi factor authentication), but I'm not exactly sure. Is there a way to bypass this
without canceling the MFA?
You cannot use interactive login inside IFRAME with MSAL, also embedded webviews can stop working for security reassons authenticathing against third party providers like Google or others (OpenID).
It's not about MFA.
CmsiInterrupt - For security reasons, user confirmation is required for this request. Because this is an "interaction_required" error, the client should do interactive auth. This occurs because a system webview has been used to request a token for a native application - the user must be prompted to ask if this was actually the app they meant to sign into. To avoid this prompt, the redirect URI should be part of the following safe list:
http://
https://
msauth://(iOS only)
msauthv2://(iOS only)
chrome-extension:// (desktop Chrome browser only)

Facebook : URL blocked This redirect failed because the redirect URI is not white-listed in the app's client OAuth settings

Recently I have used Facebook Login option in my website. I have wrote all the APIs needed and tested them thoroughly in using "localhost" as domain. While configuring settings in my APP in Facebook developers account, I have setup all the necessary settings like giving Oauth redirect URL, adding domain name in basic settings and other things. Everything worked fine then. So, I have requested required app permissions like pages_manage_posts, pages_read_enagagment, pages_show_list and applied for them. Facebook approved them in the app review.
the Redirect URL ("https://execute.app/#/socialmedia/management/") that I used in Facebook is correctly put in the Facebook Oauth redirect URL path as shown in the pic below.
I have used server side APIs for Facebook login and graph APIs. I have used Oauth2 for Facebook login. You can see the code below
var OAuth2 = require('oauth').OAuth2;
var oauth2 = new OAuth2(CONSTANTS.FB_APP_Key,
CONSTANTS.FB_APP_Secret,
"", "https://www.facebook.com/dialog/oauth",
"https://graph.facebook.com/oauth/access_token",
null);
app.get('/api/document/facebook/auth', function (req, res) {
var redirect_uri = "https://execute.app/#/socialmedia/management/";
console.log("redirect_uri ", redirect_uri);
var params = { 'redirect_uri': redirect_uri, 'scope': 'email,public_profile,pages_manage_posts,pages_show_list,pages_read_engagement' };
var authUrl = oauth2.getAuthorizeUrl(params);
res.send({
"status": true,
"message": "login url generated successfully",
"url": authUrl
});
});
I will explain the problem in two scenarios below.
Scenario-1: When there is and existing active Facebook session in browser i.e, when some user is already logged into Facebook in facebook.com or developers.facebook.com and when we try to login into Facebook from our website, Oauth Authentication API gets called and returns Facebook login URL with status code 200 and the url gets opened in a new tab, its works fine, we don't need to enter Facebook login credentials again, we can just click on "**Continue as USER**" button and then we get the login code, with which we can get user access token. After getting token everything works as planned.
Scenario-2: But if no user is already logged into Facebook in browser and when I click on **login to Facebook** button, API call is made and it returns login URL, but the response status code sent by Oauth login API is 304. A new Facebook login tab is opened, but there is a warning displaying a message saying "URL blocked.
This redirect failed because the redirect URI is not white-listed in the app's client OAuth settings. Make sure that the client and web OAuth logins are on and add all your app domains as valid OAuth redirect URIs."
But you can see that I have added correct Redirect URL in Facebook already. It works in scenario-1 and does not work in another as I mentioned above.
Note: the Facebook login URL returned by Oauth Authentication API is same regardless the status code 200 or 304 . It goes as " https://www.facebook.com/dialog/oauth?redirect_uri=https%3A%2F%2Fexecute.app%2F%23%2Fsocialmedia%2Fmanagement%2F&scope=email%2Cpublic_profile%2Cpages_manage_posts%2Cpages_show_list%2Cpages_read_engagement&client_id=88XXXXXXX663"
Please help me in solving this issue ,thanks in advance
The OAuth RFC states for the redirect URI that:
The endpoint URI MUST NOT include a fragment component.
It might be a bug in Facebook that it works for some scenarios and does not work for others, but in fact it's best to avoid a URI with a fragment component. If Facebook's documentation states that you can use redirect URIs with fragments I would try to contact them ask why this doesn't work in some scenarios.

why is getting oAUTH2 token failing in the Chrome Extension?

I have a Chrome Extension that needs to authenticate the user. Once authenticated, I will send that user's email to my server running in Docker and then log them in. I am having trouble getting the token. Here is the code:
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
if (chrome.runtime.lastError) {
currentSessionAccessToken=token;
alert(chrome.runtime.lastError.message);
//alert("you need to have a gmail account"); //ubuntu
return;
}
currentSessionAccessToken=token;
var x = new XMLHttpRequest();
x.open('GET', 'https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=' + token);
x.onload = function() {
if (x.readyState=200)
{
var data=this.responseText;
jsonResponse = JSON.parse(data);
photo = jsonResponse.picture;
szName=jsonResponse.name;
email=jsonResponse.email;
x.abort(); //done so get rid of it
send_to_backend(request, sender, sendResponse);
};
}
x.send();
}
The problem is that I am not getting back an access token. The backend (at this time) is also on my laptop (localhost) but in a docker container. I don't have an SSL cert for my localhost and I am wondering if that is the issue? I am never getting a token so I never get to send it with the XMLHttpRequest, and thus I never get a ReadyState=200. Any idea what is wrong?
Did you register your app for Google OAuth API access and designate the oauth field in the manifest?
From the documentation on user auth:
Copy key to your manifest
When you register your application in the Google OAuth console, you'll provide your application's ID, which will be checked during token requests. Therefore it's important to have a consistent application ID during development.
To keep your application ID constant, you need to copy the key in the installed manifest.json to your source manifest. It's not the most graceful task, but here's how it goes:
Go to your user data directory. Example on MacOs: ~/Library/Application\ Support/Google/Chrome/Default/Extensions
List the installed apps and extensions and match your app ID on the apps and extensions management page to the same ID here.
Go to the installed app directory (this will be a version within the app ID). Open the installed manifest.json (pico is a quick way to open the file).
Copy the "key" in the installed manifest.json and paste it into your app's source manifest file.
Get your OAuth2 client ID
You need to register your app in the Google APIs Console to get the client ID:
Login to the Google APIs Console using the same Google account used to upload your app to the Chrome Web Store.
Create a new project by expanding the drop-down menu in the top-left corner and selecting the Create... menu item.
Once created and named, go to the "Services" navigation menu item and turn on any Google services your app needs.
Go to the "API Access" navigation menu item and click on the Create an OAuth 2.0 client ID... blue button.
Enter the requested branding information, select the Installed application type.
Select Chrome Application and enter your application ID (same ID displayed in the apps and extensions management page).
Once you register your app you need to add something like this to your manifest:
"oauth2": {
"client_id": "YOUR_CLIENT_ID",
"scopes": ["scope1", ...]
}
Turns out that in order to get "identity" working you must publish to the Google WebStore. The reason I stayed away from that is that it often takes weeks to get a site reviewed. I have had that experience in the past. I haven't really nailed down the new URL that will be using and wanted to get the system working before I did that. Now that I submitted for Review, I guess I have some time, and will "dummy up" the steps needed (ie authentication) to continue the development work. Thanks Micah for pointing out the manual. This led to me realizing that there is no way to get "identity" working without getting approval from Google.

Auth0 authentication of single-page-app on a different domain than the api

I'm trying add Auth0 authentication to my single-page-app. My app is running under a domain, say app.mycompany.com, whereas the api used by this app is running under a different domain, say api.mycompany.com.
I'm aware of this thread:
Single Sign On (SSO) solution/architecture for Single Page App (SPA)
and the auth0 articles and github repositories linked by here. But I have a feeling that my scenario is slightly simpler, as I don't necessarily want to have single-sign-on between several different single-page-apps. For a start I just want the seperation between the API and the app.
Here is what I have tried already:
I already started from the article React Login With Auth0 and downloaded the starter project. I can surely login without problems and it will leave me with an id_token in my localStorage containing a JWS issued by Auth0.
I can also login directly on api.mycompany.com (my FeathersJS API application) and I can see that during the OAuth redirecting process, the id_token token is magically translated to a feathers-jwt token issued by my Feathers application containing the internal ID of the user-object matching the auth0-ID. I also have implemented the logic used to map from the Auth0-ID to my internal ID. Furthermore all my Feathers hooks such as validation of token and population of the user are working.
What I cannot figure out is how to alter the react-application running under app.mycompany.com with an Auth0-token in localStorage, so that this token is translated to a feathers-jwt token by api.mycompany.com, in such a way that all succeeding API-calls automatically has the feathers-jwt token included so the API can validate the user and return the right data.
Any suggestions on how to proceed will be greatly appreciated.
A couple of more background details:
The api is built on node.js and featherjs (which basically is an extension of Express)
The single-page-app is built on ReactJS and is served by a simple Express server, but it could be served by any server that can serve static files over http. The single-page-app makes http-requests to the api to read data and perform operations.
The api has the following lines of code taking care of the authentication:
const authentication = require('feathers-authentication');
const Auth0Strategy = require('passport-auth0').Strategy;
app.configure(authentication({
local:false,
token: {
secret: 'mysecret',
payload: ['email', 'auth0Nickname'],
issuer: 'mycompany'
},
idField: 'id',
shouldSetupSuccessRoute: false,
auth0: {
strategy: Auth0Strategy,
domain: 'mycompany.eu.auth0.com',
'clientID': 'xxx',
'clientSecret': 'yyy'
}
}));
I had exactly the same problem as you, I wanted to authenticate a user from a single page application, calling the API located on an other server.
The official auth0 example is a classic Express web application that does authentication and renders html page, but it's not a SPA connected to an API hosted on an other domain.
Let's break up what happens when the user authenticates in this example:
The user makes a request calling /auth/auth0 route
The user is automatically redirected to the Auth0 authentication process (Auth0 login form to choose the provider and then the provider login screen)
The user is redirected to /auth/success route
/auth/success route redirects to the static html page public/success.html, also sending a jwt-token cookie that contains the user's token
Client-side, when public/success.html loads, Feathers client authenticate() method reads the token from the cookie and saves it in the local storage.
From now, the Feathers client will authenticate the user reading the cookie from the local storage.
I tried to adapt this scenario to a single-page application architecture, implementing the following process:
From the SPA, call the authentication API with a source query string parameter that contains the SPA URL. For example: http://my-api.com/auth/auth0?source=http://my-spa.com
Server-side, in /auth/auth0 route handler, create a cookie to store that URL
After a successful login, read the source cookie to redirect the user back to the SPA, sending the JWT token in a cookie.
But the last step didn't work because you can't set a cookie on a given domain (the API server domain) and redirect the user to an other domain! (more on this here on Stackoverflow)
So actually I solved the problem by:
server-side: sending the token back to the client using the URL hash.
client-side: create a new html page that reads the token from the URL hash
Server-side code:
// Add a middleware to write in a cookie where the user comes from
// This cookie will be used later to redirect the user to the SPA
app.get('/auth/auth0', (req, res, next) => {
const { origin } = req.query
if (origin) {
res.cookie(WEB_CLIENT_COOKIE, origin)
} else {
res.clearCookie(WEB_CLIENT_COOKIE)
}
next()
})
// Route called after a successful login
// Redirect the user to the single-page application "forwarding" the auth token
app.get('/auth/success', (req, res) => {
const origin = req.cookies[WEB_CLIENT_COOKIE]
if (origin) {
// if there is a cookie that contains the URL source, redirect the user to this URL
// and send the user's token in the URL hash
const token = req.cookies['feathers-jwt']
const redirectUrl = `${origin}/auth0.html#${token}`
res.redirect(redirectUrl)
} else {
// otherwise send the static page on the same domain.
res.sendFile(path.resolve(process.cwd(), 'public', 'success.html'))
}
})
Client-side, auth0.html page in the SPA
In the SPA, I created a new html page I called auth0.html that does 3 things:
it reads the token from the hash
it saves it in the local storage (to mimic what the Feathers client does)
it redirects the user to the SPA main page index.html
html code:
<html>
<body>
<script>
function init() {
const token = getToken()
if (!token) {
console.error('No auth token found in the URL hash!')
}
// Save the token in the local storage
window.localStorage.setItem('feathers-jwt', token)
// Redirect to the single-page application
window.location.href = '/'
}
// Read the token from the URL hash
function getToken() {
const hash = self.location.hash
const array = /#(.*)/.exec(hash)
if (!array) return
return array[1]
}
init()
</script>
</body>
</html>
And now in the SPA I can use the Feathers client, reading the token from the local storage when the app starts.
Let me know if it makes sense, thank you!
If you haven't done so, you should follow this article (React Login with Auth0) to implement the authentication on your React application. If you already tried to follow it, update your question with specific issues you faced.
Even though you currently not need SSO, the actual implementation of the authentication in your application will not vary much. By using Auth0 enabling SSO across your apps is mostly enabling configuration switches.
Finally for a full reference with all the theory behind the security related aspects of your exact scenario check:
Auth0 Architecture Scenarios: SPA + API
Update:
The full scenario I linked too covers the most comprehensive scenarios where an API is accessed by a multitude of client applications that may even be developed by third-parties that do not own the protected API, but want to access the data behind it.
It does this by leveraging recent features that are currently only available in the US region and that at a very high level can be described as an OAuth 2.0 authorization server delivered as a service.
Your particular scenario is simpler, both the API and client application are under control of the same entity, so you have another option.
Option 1 - Leverage the API authorization through Auth0 US region only (for now)
In this situation your client application, at authentication time, would receive an id_token that would be used to know the currently authenticated user and would also receive an access_token that could be used to call the API on behalf of the authenticated user.
This makes a clear separation between the client application and the API; the id_token is for client application usage and the access_token for API usage.
It has the benefit that authorization is clearly separated from authentication and you can have a very fine-grained control over authorization decisions by controlling the scopes included in the access token.
Option 2 - Authenticate in client application and API in the same way
You can deploy your client application and API separately, but still treat them from a conceptual perspective as the same application (you would have one client configured in Auth0 representing both client-side and API).
This has the benefit that you could use the id_token that is obtained after authentication completes to know who the user was on the client-side and also as the mechanism to authenticate each API request.
You would have to configure feathers API to validate the Auth0 id_token as an accepted token for accessing the API. This means that you don't use any feathers based on authentication on the API, that is, you just accept tokens issued by Auth0 to your application as the way to validate the access.

chrome.identity.LaunchWebAuthFlow: How to implement logout in a web app using oauth2

I am working on some client side web app like a chrome extension that needs access to outlook mail and calendar. I followed the instruction on https://dev.outlook.com/RestGettingStarted and successfully got access and refresh tokens to retrieve data.
However, I cannot find any way of implementing "logout". The basic idea is to let user sign out and login with a different outlook account. In order to do that, I removed cached tokens, requested access tokens in interactive mode. The login window did pop out, but it took any valid email address, didn't let me input password and finally returned tokens for previous account. So I was not able to really use a different account until the old token expired.
Can anyone please tell me if it is possible to send a request to revoke the tokens so people can use a different account? Thanks!
=========================================================
Update:
Actually it is the fault of chrome.identity api. I used chrome.identity.LaunchWebAuthFlow to init the auth flow. It caches user's identity but no way to remove it. So we cannot really "logout" if using this api.
I used two logouts via launchWebAuthFlow - first I called the logout link to my app, then secondly, I called the logout link to Google.
var options = {
'interactive': false,
'url': 'https://localhost:44344/Account/Logout'
}
chrome.identity.launchWebAuthFlow(options, function(redirectUri) {});
options = {
'interactive': false,
'url': 'https://accounts.google.com/logout'
}
chrome.identity.launchWebAuthFlow(options, function(redirectUri) {});

Resources