Google API on Published Electron app: gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy - reactjs

I have a React + Electron app using Google API to authenticate and get a list of calendar events.
The API script is being loaded on the head of my index.html and initialised on my App.js like so:
// Initializes the API client library and sets up sign-in state listeners.
initClient() {
let gapi = window["gapi"];
let that = this;
gapi.load("client", start);
function start() {
gapi.client
.init({
apiKey: GOOGLE_API_KEY,
clientId: CLIENT_ID,
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"
],
scope: "https://www.googleapis.com/auth/calendar.readonly"
})
.then(() => {
gapi.auth2
.getAuthInstance()
.isSignedIn.listen(that.updateSigninStatus);
that.updateSigninStatus(
gapi.auth2.getAuthInstance().isSignedIn.get()
);
that.setState({
apiLoaded: true
});
});
}
}
It works completely fine on a local environment, where I have a server running, but once I build my Electron app and run the app "natively", I get the following error: gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy
I don't have an advanced understanding of APIs and Servers to figure this out but through research, I found something about the API not working from a "file://" protocol, which is the case on an Electron app.
Thoughts? Ideas?

Related

React axios - calling a http endpoint from https webapp

am trying to call a http endpoint from my react web application created using create react app
i am given the below code and a local pem file
caCrt = fs.readFileSync('./ca-crt.pem')
const httpsAgent = new https.Agent({ ca: caCrt , keepAlive: false })
axios.get(url, { params: params, httpsAgent: httpsAgent {color:#0747a6}}) .then( res =>
not sure how i can call it properly from frontend (https web app), i received several "mixed content" error from chrome, understand that i may not be able to use "fs" module from my react app

Google SignIn: Redirect Uri in GSI client flow

I’m trying to use the new Google Identity Services to sign in an user to get access to manage his calendars.
My current auth function looks like this in JS:
const auth = async () => {
return new Promise((resolve) => {
const GTokenClient = google.accounts.oauth2.initTokenClient({
client_id: GOOGLE_CLIENT_ID,
scope: GOOGLE_CALENDAR_SCOPE,
prompt: '',
callback: resolve
});
if (gapi.client.getToken() === null) {
GTokenClient.requestAccessToken({ prompt: 'consent' });
} else {
GTokenClient.requestAccessToken({
prompt: ''
});
}
});
};
In desktop web browsers it works fine and the promise resolves but in smartphones (currently trying with an iPhone12) the browser opens a new tab and it stays there loading after giving permissions.
I’m aware that you can set a redirect with the code flow, but it is possible to do the same with the client flow?
I don’t know what to do honestly because there are no examples to implement this behavior using the client flow in the Google documentation.
I only want to be able to redirect the user to the initial screen, if I close the tab that google creates for signing in the calendar is loaded and everything seems fine, it is just a matter of redirection.

Create React App proxy to express server for PassportJS not working

spent a couple of days attempting to set up a proxy for my react app to my express backend where I am using passportjs for gooogle social auth.
react dev server on PORT 3000
express server on PORT 5000
When I click on the button, it reloads the page, but does not start the passportJS google auth process (i.e. does not redirect to the oauth2 flow).
<Button href='/auth/google'> Link Button </Button>
curl is properly proxying the calls from port 3000 to 5000
PassportJS process works properly when I go directly to the express server endpoint I created here: http://localhost:5000/auth/google
Key code pieces are below that should allow proxy to work from react frontend to express passportJS Oauth2 flow.
package.json
"proxy": "http://localhost:5000"
app.js
<a href="auth/google/" target="">
<Button> Link Button </Button>
</a>
server.js
app.get('/auth/google',
passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid'] }),
);
setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy('/auth', { target: 'http://localhost:5000/' }));
};
I did not end up needing the package.json proxy entry. These are all of the pieces together that got everything to work as expected.
I have the front end application in a /client directory which is where I used create react app.
From package.json in the create react app (client directory) I deleted this as the http-proxy-middleware seems to require commonjs imports
"type": "module",
setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy('/auth/google', { target: 'http://localhost:5000/' }));
};
App.js
using material UI button
<Button href="/api/auth/google" variant="contained" color="primary">
Index.js for passportJS config and express server
in the passportGoogle.strategy options set this to be consistent with the rest of the config
callbackURL: `/api/auth/google/callback`
app.get('/api/auth/google',
passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid'] }),
);
app.get('/api/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/failed' }),
function(req, res) {
res.redirect('http://localhost:3000');
}
);
Google console
URIs
http://localhost:3000
Authorized redirect URIs
http://localhost:3000/api/auth/google/callback

Calling an Azure AD secured Azure Function from React SPA/Azure Static Web App

I'm developing an SPA with React that is hosted as an Azure Static Web App. The App is secured with Azure AD Authentication which works great, I already built a Login that works fine and I can call Azure (Graph) APIs with the token I got and retrieve information for the granted scopes (e.g. user profile picture). To achieve this, I'm using a wrapper called React AAD MSAL which neatly wraps the Microsoft Authentication Library (msal#1.4.0).
So far so good, no problems here. But I'm in need of a backend, of course. I decided to this with Azure Functions, since serverless is the best way for me here. So I made a quick HTTP trigger protoype that runs in Azure as Azure Function und works when I call the URL with the correct paramters.
But of course the Azure Function needs to be secured, so only my React App can call this function. So I thought there should be way to do this through Azure AD, since my user is already logged in as such.
I tried and tried and tried different ways I found online but none of them seem to work or I am doing something wrong.
The general tutorial I tried to follow is this one from MS itself. I tried using the "Express" setting which of course didn't work. I tried the advanced configuration, which also didn't work. The advanced tutorial says you need to have an App registration for the service, I'm not even sure if this is can be my Static Web App or a new on (I tried both with no success). Isn't it enough to tell the Azure Function that it is now AAD secured and may only accept calls from a source secured by an access token that contains the App ID of my app, which is provided in the settings? You can easily provide all these settings, it just doesn't seem to work.
So I'm stalling very early on here. To call the function itself, I first need to get a Authorization Token. According to this tutorial from MS (see "Validate tokens from providers"), I need to send an access token which I got when logging in to my SPA Web App to the Azure Function endpoint ending in .auth/login/aad. Getting this token is easy, since React AAD MSAL provides a method authProvider.getAccessToken() which I can use to extract it. I'm then making a POST request to https://<My Azure Function URI>/.auth/login/aad with the access token in the body as JSON { 'access_token': authToken.accessToken }. I should be getting an Authentication Token which I can then use to call the actual function, but I always get the same response, no matter what I try: You do not have permission to view this directory or page.
So this is where I am. I tried different methods and solutions I found to no avail. Maybe I did something wrong from the ground up, maybe I'm using the wrong methods, I really don't know at this point. Does anyone have experience with this? Is there something wrong in my general approach, do I need to do something else? Or is it just something in the configuration I need to change?
Edit
Since it was asked, here's how I retrieve the token. The concept behind this is using a redux-thunk to dispatch an asynchronous action to the react-redux store. I simplified it not only for this question here but for my testing as well. Right now I'm only trying to get the authentication token and log the answer the POST request is giving me:
import { authProvider } from '../../Authentication/AuthProvider';
//Fetch
async function getAccessToken(authToken) {
const body = { 'access_token': authToken.accessToken };
fetch('https://<My Azure function URL>/.auth/login/aad', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body)
},
).then(response => {
console.log(response);
});
}
export const fetchAddressData = () => async dispatch => {
const token = await authProvider.getAccessToken();
await getAccessToken(token);
// The actual call to the Azure function will go here once we have an Authentication Token
}
The authProvider is a component from react-aad msal and the configuration looks like this:
import { MsalAuthProvider, LoginType } from 'react-aad-msal';
//MSAL Config
const config = {
auth: {
authority: '<Tenant ID>',
clientId: '<Client ID from App registration (Azure Static Web App)>',
redirectUri: window.location.origin
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
// Authentication Parameters
const authenticationParameters = {
scopes: [
'openid',
'user.read',
'https://<Azure Function URI>/user_impersonation'
],
forceRefresh: true
}
// Options
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin
}
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)
Edit 2
I tweaked some additional settings trying to work with the user impersonation, still no success. Here's an overview over my current Azure settings that are important for this (did I forget any?).
Azure Function:
Authentication is activated, AAD auth only, advanced settings:
Azure Function - App Registration:
Authentication settings:
Client secret:
Expose an API - Exposing user_impersonation API so the Web App can consume it:
Azure Static Web App (React SPA) - App Registration:
Application URI ID which is used as Token Audience in the Azure Function (advanced authentication setting):
API permissions - using the user_impersonation API which is exposed by the Azure Function App Registration:
Is there anything wrong in this configuration? It mostly likely is, but I don't know what since I followed the tutorial on MSDN. I only added the user_impersonation afterwards since it didn't work.
According to the information provided, you do not configure right scope in your authProvider file. You need to add the scope you define when you create AD application to protect function. So please update the scope as scopes: ["openid","<your function app scope>"] in authProvider.
For example
Create Azure AD application to protect function
Register Azure AD application. After doing that, please copy Application (client) ID and the Directory (tenant) ID
Configure Redirect URI. Select Web and type <app-url>/.auth/login/aad/callback.
Enable Implicit grant flow
Define API scope and copy it
Create client secret.
Enable Azure Active Directory in your App Service app
Create Client AD application to access function
Register application
Enable Implicit grant flow
configure API permissions. Let your client application have permissions to access function
Code
authProvider
import { MsalAuthProvider, LoginType } from "react-aad-msal";
import { Logger, LogLevel } from "msal";
export const authProvider = new MsalAuthProvider(
{
auth: {
authority: "https://login.microsoftonline.com/<tenant>",
clientId: "<>",
postLogoutRedirectUri: window.location.origin,
redirectUri: window.location.origin,
validateAuthority: true,
navigateToLoginRequestUrl: false,
},
system: {
logger: new Logger(
(logLevel, message, containsPii) => {
console.log("[MSAL]", message);
},
{
level: LogLevel.Verbose,
piiLoggingEnabled: false,
}
),
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true,
},
},
{
scopes: [
"openid",
"<the scope you define for your function>",
],
forceRefresh: true,
},
{
loginType: LoginType.Popup,
tokenRefreshUri: window.location.origin + "/auth.html",
}
);
Call API
const CallAPI= async () => {
// You should should use getAccessToken() to fetch a fresh token before making API calls
const authToken = await provider.getAccessToken();
console.log(authToken.accessToken);
let body = { access_token: authToken.accessToken };
let res = await fetch(
"<your function url>/.auth/login/aad",
{
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}
);
let data = await res.json();
console.log(data);
body = { name: "Azure" };
res = await fetch("<>", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"X-ZUMO-AUTH": data["authenticationToken"],
},
body: JSON.stringify(body),
});
data = await res.text();
console.log(data);
};
I was dealing with the same issue for a while. If your sure you are getting the right access token and and passing it correctly, then look into the configuration in the portal. If you automatically created the app registration for the function app, Check how the ISSUER URL is set up. You can find this in the function app>authentication>edit. make sure that the url does not have /v2.0 at the end. Azure function only work with the default(/v1.0) route.

Angular PouchDb and Auth Example

I tried great example angularjs todo app:
https://github.com/danielzen/todo-ng-pouchdb
and now I'm trying use it with some authentication plugin, but without success ( https://github.com/nolanlawson/pouchdb-authentication ). Todo app use some old angular-pouchdb lib.
Please do you have any tip to example where is used angular, pouchdb and auth plugin to login, signup to couchdb.
My problems with log into CouchDb were because of wrong auth params, I didn't wrap login values into array with key auth.
So just example of correct call to server:
$scope.sync = $scope.tasks.$db.replicate.sync('http://www.server.info:5984/' + dbName,
{live: true, "auth": {username:"john", password:"secret"}})
.on('error', function (err) {
});

Resources