Getting "state not found" during msal loginRedirect method - msal.js

I am attempting to implement msal 2 in my React application. I'm using the msal-react-samples/default as the template for my work. I am able to see me login page and login using the loginRedirect method. But upon returning to my application I'm getting the following error. I saw that someone asked a very similar question (65375241). But it looks like the user abandoned it. I have included additional information below.
errorCode: "state_not_found"
errorMessage: "State not found: Cached State"
Additional Information
Authenticating into ADB2C
MsalConfig:
{ auth: { clientId: "REDACTED" authority: "REDACTED" knownAuthorities:["Redacted, Same as authority"], redirectUri: "http://localhost:3000/AuthTest", navigateToLoginRequestUrl: false }, cache: { cacheLocation: "sessionStorage", storeAuthStateInCookie: false, }, system: { loggerOptions: { ...(logger options from sample) } }
Authentication worked previously with MSAL1.
I'm aware that as of 1/7/2021 the msal-react package is not meant for production environments. I could attempt to change to just use just the msal-browser package if that would help.

I was able to figure out my own error by looking at the the MSAL git repo issue
#azure/msal-browser redirect not working correctly
I will describe my issue here to assist anyone else that has this same error.
It was caused because I had used the addEventCallback method improperly.
I had it implemented in my code as a simple function call. But it needs needs to be put into a useEffect and also issue the removeEventCallback method after consuming it.
I had
instance.addEventCallback((message) => {
console.log(message)
})
rather than
useEffect(() => {
const callbackId = instance.addEventCallback((message) => {
console.log(message)
})
return () => {
if (callbackId) {
instance.removeEventCallback(callbackId);
}
}
}, [instance])

Related

I'm having issues using the credential manager through chrome extension

I am trying to integrate the credential manager API into my extension so I can save the API credentials using it but am having issues.
Just for testing purposes, I just tried saving the credentials from the popup (Where the user login happens) and when I tried that I got an error saying "NotSupportedError: The user agent does not support public key credentials." I did some googling but didn't find what I was looking for. I thought it might have something to do with the popup so I tried doing the same through the background script but it can't access the window object so that didn't work.
I haven't yet tried doing this though a content script which I would imagine would work but ideally I'd prefer to just store it when the user logs in and then just call it as I need it.
Here is the basic code I'm using for testing that I grabbed from the MDN site;
if ("PasswordCredential" in window) {
let cmAPICredential = new PasswordCredential({
id: "extension",
name: uname,
password: pwd
});
navigator.credentials.store(cmAPICredential).then(() => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Credentials stored in the user agent's credential manager.`, cpage: "main.js" }, function(response) {});
}
}, (err) => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Error while storing the credential: ${err}`, cpage: "main.js" }, function(response) {});
}
});
} else {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Password credential isn't supported in this browser`, cpage: "main.js" }, function(response) {});
}
}
Is there any way that I can do what I am attempting? The only thing I really want to secure is the password and this seems to be the only good way to do it through a chrome extension.
Based on #wOxxOm response and some other research, it looks like I can't use the credential management API with chrome extensions.
So the answer to my question is you can't.

Test suite for smart home doesn't pass with one action, but it does with another one with the exact same code

we've developed a smart home action for Google Assistant and our "staging action" passed the test suite for smart home. Now that we wanted to get the "production action" certified, the test suite didn't pass, not a single command worked. The devices' states changed correctly, but the test suite returned the following error: AssertionError: Expected state to include: {"on":true}, actual state: {}: expected false to be true
We've had the problem before with the "staging action" and identified it to be a HomeGraph Sync problem. We fixed it and afterwards it worked. The "production action's" code is the exact same as the "staging action's" and the settings are also the same. So we have no idea what we could do to make the test suite pass. Does anyone have an idea of what the problem might be?
Here is an excerpt of our code:
const { smarthome } = require('actions-on-google');
// creating app in constructor
this._app = smarthome({ jwt, debug: this._options.debug || false });
// code-snipped sending state to HomeGraph after creating the state object from our device states
console.info('Report state:');
console.info(inspect(state, {depth: null, showHidden: false}));
this._app.reportState(state)
.then(res => console.info('report state succeeded', res))
.catch(err => console.error('report state failed', err));
And here is the corresponding log:
Report state:
{
requestId: 'c82c0c20-a10d-4ded-a456-319109188501',
agentUserId: '730f1842168ade4dcf9be3e40ea279e7ac4e6f21c002c09909f9d9a0341d2214',
payload: {
devices: {
states: {
'500dc011398fd3c191efdfa8e13a1062f7ef4d1c': { on: true, online: true }
}
}
}
}
report state succeeded {
"requestId": "c82c0c20-a10d-4ded-a456-319109188501"
}
In case of an error we expect something like:
404: agentUserId unknown or HomeGraph not enabled
401: access rights misconfigured
or any other error
but it succeeds.
EDITED:
We adjusted our code by reading the state from HomeGraph directly after sending it to HomeGraph in order to see whether HomeGraph gets anything from us at all:
const auth = new google.auth.GoogleAuth({
credentials: jwt,
scopes: ['https://www.googleapis.com/auth/homegraph']
});
this._hg = google.homegraph({
version: 'v1',
auth: auth,
});
this._app = smarthome({ jwt, debug: this._options.debug || false });
this._app.reportState(state).then(async res => {
const request = {
requestBody: {
agentUserId: state.agentUserId,
inputs: [{
payload: {
devices: Object.keys(state.payload.devices.states).map(id => ({id}))
}
}]
}
};
console.info(require('util').inspect(request, {depth: null}));
const readStates = await this._hg.devices.query(request);
console.info('########## STATES in HOMEGRAPH #######################');
console.info(readStates.data);
return res;
})
.catch(err => console.error('report state failed', err));
The state is empty.
We also implemented a simple node.js app to read all device states via HomeGraph API. All device states are empty at any time.
The main question is: Why does calling this._app.reportState(state) never run into catch handler? There must be something wrong with our sending the state to HomeGraph, but we do not get any error back...?
Thanks! The problem has sorted itself out somehow. Maybe Google had a bug, but without doing anything on our part the tests pass now.
If you're getting {} as the actual state value, then it seems like the Test Suite is unable to read data from the Home Graph. There are two likely reasons for this.
In your production project, your test account has a different agentUserId
Your production project will have a different service account key than your staging project. You may be uploading the wrong key, which causes the Home Graph API calls in the test suite to be unsuccessful.

How to list images and tags from the gcr.io Docker Registry using the HTTP API?

I'm trying to fetch a list of available images and their tags from Google Container Registry (gcr.io) in Node.js.
I first use google-auto-auth to optain a token with scope https://www.googleapis.com/auth/devstorage.read_write,
and I exchange that token for a gcr.io token like so:
axios.get('https://gcr.io/v2/token?service=gcr.io', {
auth: {
username: '_token',
password: token // token I got from `google-auto-auth`
}
})
I then try to use this to call the v2/_catalog endpoint:
axios.get('https://gcr.io/v2/_catalog', {
headers: {
Authorization: `Bearer ${gcrToken}`
}
})
And I get the following error:
{
errors: [ { code: 'DENIED', message: 'Failed to retrieve projects.' } ]
}
Fair enough, it must require my project ID, but where am I supposed to provide it?
Just to see if I could get anything else working, I tried:
axios.get('https://gcr.io/v2/my-project-id/my-image/tags/list', {
headers: {
Authorization: `Bearer ${gcrToken}`
}
})
And I get the following back:
{
errors: [
{
code: 'NAME_INVALID',
message: 'Requested repository does not match bearer token resource: my-project-id/my-image'
}
]
}
How can I read image info from gcr.io?
After extensive communication with Jon Johnson from GCR, we finally figured out what was wrong. If you upvote this answer, please upvote Jon's as well, he went above and beyond to get this issue resolved.
Most of this stuff is undocumented as of this writing.
need to use the registry:catalog:* scope.
my images were pushed to us.gcr.io, and they treat them as separate registries — I thought they were mirrors.
the service account must have the Project Viewer role in Google Cloud IAM.
you can use a GCR token, as well as a Google Cloud token. However while the GCR token cannot be used with Basic base64(_token:<token>), the Google Cloud token can.
Getting the GCR token
// Updated host
axios.get('https://us.gcr.io/v2/token?service=gcr.io', {
params: {
service: 'us.gcr.io',
scope: `registry:catalog:*`
},
auth: {
username: '_token',
password: token // token I got from `google-auto-auth`
},
})
Using the token to list repositories
const client = axios.create({
baseURL: `https://us.gcr.io/v2`,
headers: {
Authorization: `Bearer ${token}`
}
})
client.get('/_catalog').then((response) => {
console.log(response.data.repositories)
})
The first error is likely because you're missing one of the scopes for listing projects:
https://cloud.google.com/resource-manager/reference/rest/v1/projects/list#authorization
You get the second error because you're missing the scope in your token exchange.
You want something like:
https://gcr.io/v2/token?service=gcr.io&scope=repository:<my-project-id/my-image>:*
See the example here: https://docs.docker.com/registry/spec/auth/token/#requesting-a-token

Azure Functions - Can it be used with Office Add-in's?

I'm sure there are clever people who can make anything run on Azure functions but does it make sense at all to use it for running Office Add-in's? I read that it's ideal for running small pieces of code and that's what my add-in currently running on Azure as an web app is.
You wouldn't use Azure Functions to build an add-in -- but you absolutely could use it in conjunction with a regular website, for some small server-side processing.
Concrete example: for an Add-in that a colleague and I were building, we needed to obtain a user's GitHub permissions to post Gists on the user's behalf. GitHub uses an "authorization code grant type" flow (see https://developer.github.com/v3/oauth/), so the flow would be as follows:
We pop a dialog (using the recently-introduce Dialog API in Add-ins) to direct the user to https://github.com/login/oauth/authorize, which shows a pretty login UI.
If the user signs in and consents, GitHub sends an authorization code back to us. The code does little good for us in client-side JavaScript, but if we pass it to an Azure Function, we can exchange it for an access token. This must be done in some server-side code (i.e., a web server, or Azure Functions, as a super-lightweight form of a web server) so that we can pass in a Client Secret for the exchange -- which quite naturally wouldn't be secret in sniffable-out client-side JavaScript. Hence putting that code on the server.
If you're curious to see what that code was like, here it is:
var request = require('request');
module.exports = function (context, data) {
context.log('code: ' + data.code);
if ('code' in data) {
request.post({
url: 'https://github.com/login/oauth/access_token',
json: {
client_id: '################',
client_secret: '################',
redirect_uri: '################',
code: data.code
}
}, function (err, httpResponse, body) {
if (err) {
context.log('error: ' + err);
context.res = {
body: {
status: 500,
error: err
}
}
}
else {
context.res = { body: body };
}
context.done();
});
}
else {
context.res = {
status: 400,
body: { error: 'Please pass the GitHub code in the input object' }
};
context.done();
}
}

Google+ insert moment with nodejs client

Has anyone been able to get the google-api-nodejs-client to successfully insert a moment?
Whatever I try, I get a generic 400 "Invalid value" error but am unable to narrow down the invalid value because the API Explorer doesn't work either.
Would it be because of the missing data-requestvisibleactions parameter? I'm using passport.js's require('passport-google-oauth').OAuth2Strategy for handling oauth access, and that part is working fine, but I have no idea how to incorporate requestvisibleactions into the oauth request flow since this is definitely not originating from a clientside form.
Here's a snippet of what I'm trying to do (using the latest version of googleapis, v1.0.2):
var google = require('googleapis')
var auth = new google.auth.OAuth2()
auth.setCredentials({
'access_token': user.token
})
google.plus('v1').moments.insert({
collection: 'vault',
userId: 'me',
debug: true,
resource: {
type: "http://schemas.google.com/AddActivity",
target: {
type: "http://schema.org/CreativeWork",
url: "...omitted...",
image: "...omitted...",
description: "test",
name: "test"
}
},
auth: auth
}, function (err, response) {
if (err) {
console.error(err)
res.send(err.code, err)
} else {
console.log(response)
res.send(200)
}
})
ref 1 (out-of-date w.r.t. an older version of googleapis)
ref 2 (client-side, where the use of data-requestvisibleactions is more obvious)
As you speculated, you need the request_visible_actions parameter as part of the URL calling the oauth endpoint.
It looks like the current version of passport-google-oauth doesn't support this parameter. Judging by several of the open issues and pull requests, it isn't clear that the author will respond to requests to add it either. You have two possible options:
Switch to using the OAuth support that is included in google-api-nodejs-client
Patch the passport-google-oauth code. (And possibly submit a pull request in the hopes it will be useful to someone else.)
I don't use passport.js or the passport module in question, so I can't test this, but based on the github repository, I think you can insert the following in lib/passport-google-oauth/oauth2.js after line 136 and before the return statement:
if (options.requestVisibleActions) {
// Space separated list of allowed app actions
// as documented at:
// https://developers.google.com/+/web/app-activities/#writing_an_app_activity_using_the_google_apis_client_libraries
// https://developers.google.com/+/api/moment-types/
params['request_visible_actions'] = options.requestVisibleActions;
}

Resources