I am having problems getting MSAL(x) working - The login popup succeeds, but when I try to retrieve and access token from the id token using acquireTokenSilent, it causes the app to reload (all resources, per dev tools network tab), and throws an error 'Token renewal operation failed due to timeout: null'. I've searched for relevant SO questions / google, but have had no luck finding similar issues. The crazy thing is, it WORKED the other day and just stopped - even reverting to the same code does not resolve the issue.
Using acquireTokenPopup in the silent's error handler displays a popup, but won't allow login with the same user ('We don't recognize this domain name') but shows the correct MS App name. This is driving me crazy.
Relevant code (in a React component click handler):
onMSLogin() {
const { msLoginFailure } = this.props;
const userAgentApplication = this.userAgentApplication;
userAgentApplication.loginPopup(['user.read'])
.then(function () {
console.log('User login success');
const scopes = ['User.Read'];
userAgentApplication.acquireTokenSilent(scopes).then((accessToken) => {
console.log('Access token acquired (silent): ', accessToken);
this.getGraphData(accessToken);
}, (error) => {
console.error('Silent token fail: ', error);
userAgentApplication.acquireTokenPopup(scopes).then((accessToken) => {
console.log('Access token acquired (popup): ', accessToken);
});
})
}, function (error) {
// handle error
console.log('MS Login Failure: ', error);
if (msLoginFailure) msLoginFailure(error);
});
}
I believe your issue is related to MSAL.js Issue #106, and is patched within the 'dev' branch. I've been working with Microsoft the past few weeks on this library, and I've been told that an official release is due to be cut this week.
Related
My project uses Firebase authentication and storage, and is built using React JS.
I have a file which is in the storage bucket at:
gs://myproject.appspot.com/myfolder/fileId
The download URL with token is:
https://firebasestorage.googleapis.com/v0/b/myproject.appspot.com/o/myfolder%2FfileId?alt=media&token=myToken
I want to be able to download the file in my app, so I have the following function within the Firebase class:
doDownloadFile = (path: string) => {
app
.storage()
.ref()
.child(path)
.getDownloadURL()
.then((url) => {
console.log(url);
})
.catch((error) => {
console.log('Error: ', error);
});
};
(at the moment I'm just getting the URL, not downloading it).
The value of path is the downloadURL as given above (I've checked this by logging it at the top of the doDownloadFile function, and it's being passed in ok).
When I run this code, I get the following error:
Firebase Storage: User does not have permission to access 'https:/firebasestorage.googleapis.com/v0/b/myproject.appspot.com/o/myfolder%2FfileId?alt=media&token=myToken'. (storage/unauthorized)
On Googling I found 2 general solutions to this, but neither have worked for me.
The first is to set the security rules on the storage in the Firebase console. I've set this to:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth!=null;
}
}
}
The second is to add the user firebase-storage#system.gserviceaccount.com as Storage Admin in the GCP console. This has been done.
Neither of these has made any difference, though. I still get the same error.
Where am I going wrong?
I'm working on integrating web-push notifications in my web-application. Everything works fine for Chrome and Firefox on desktop and Chrome on Android, but not for Firefox for Android. This question seems to discuss the same issue but has no responses.
I used this tutorial as a base for the service worker registration script. I have added some more prints/checks but it is mostly the same.
So, when calling the registerServiceWorker method from a button press on FF Android, the serviceWorker is installed, the subscribeUser function is called, but the pushManager.subscribe method will fail with the following error message:
DOMException: User denied permission to use the Push API.
This is not correct, even while paused on the error print line Notification.permission will return "granted".
Doing the same thing on the nightly build results in slightly different, but still incorrect behaviour. The pushManager.subscribe method does not throw an error. Instead the callback is ran but with a null value for the subscription argument. Therefore, the process still fails.
Service worker registration script:
'use strict';
function updateSubscriptionOnServer(subscription, apiEndpoint) {
return fetch(apiEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
subscription_json: JSON.stringify(subscription)
})
});
}
function subscribeUser(swRegistration, applicationServerPublicKey, apiEndpoint) {
// It seems browsers can take the base64 string directly.
// const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
console.log(`Subscribing pushManager with appkey ${applicationServerPublicKey}`);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerPublicKey
})
.then(function(subscription) {
console.log('User is subscribed.');
console.log(`Sending subscription data to server (${apiEndpoint})`, subscription);
return updateSubscriptionOnServer(subscription, apiEndpoint);
})
.then(function(response) {
if (!response.ok) {
throw new Error('Bad status code from server.');
}
return response.json();
})
.then(function(responseData) {
console.log(responseData);
if (responseData.status!=="success") {
throw new Error('Bad response from server.');
}
})
.catch(function(err) {
// FF Android says "User denied permission to use the Push API."
console.log('Failed to subscribe the user: ', err);
console.log(err.stack);
});
}
function registerServiceWorker(serviceWorkerUrl, applicationServerPublicKey, apiEndpoint){
let swRegistration = null;
if ('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is supported');
console.log(`Current Notification.permission = ${Notification.permission}`);
swRegistration = navigator.serviceWorker.register(serviceWorkerUrl)
.then(function(swReg) {
console.log('Service Worker is registered', swReg);
console.log(`Current Notification.permission = ${Notification.permission}`); // Will give "granted"
subscribeUser(swReg, applicationServerPublicKey, apiEndpoint);
})
.catch(function(error) {
console.error('Service Worker Error', error);
});
} else {
console.warn('Push messaging is not supported');
return false;
}
return swRegistration;
}
I cannot figure out how to get a working push-subscription. As said before, all other browsers that I have tried work fine. I hope someone can point me in the right direction. Is this a bug in Firefox Android or in my code?
Showing notifications manually using
new Notification("Hi there!");
does work, proving in principle that permissions are not the issue.
UPDATE:
FF Fenix team confirmed a bug while displaying the notifications
Feel free to track it here
Got curious regarding service worker support for Firefox mobile browser.
Tried hard to find a debug tool for Mobile Firefox as plugin or a 3rd party tool with no luck.
However, I've tested my web push application on Mobile Firefox and may totally confirm there is an issue with Service Worker Registration state.
In order to discard any issues within my code, I've used Matt Gaunt's Webpush Book
And I can tell Matt's service worker returns registration as simply as that:
async function registerSW() {
await navigator.serviceWorker.register('/demos/notification-examples/service-worker.js');
}
function getSW() {
return navigator.serviceWorker.getRegistration('/demos/notification-examples/service-worker.js');
}
Firefox for Android successfully requests permission for notification, but it doesn't display push whenever you launch the .showNotification function.
Here's an example of showNotification method within the Badge example in Matt's website:
async function onBadgeClick() {
const reg = await getSW();
/**** START badgeNotification ****/
const title = 'Badge Notification';
const options = {
badge: '/demos/notification-examples/images/badge-128x128.png'
};
reg.showNotification(title, options);
/**** END badgeNotification ****/
}
Looks fine and should be working fine, but for Firefox Mobile it doesn't work at all.
I guess this issue should be escalated to Mozilla.
UPDATE
New bug report was created on Github:
After upgrading a react-native from 0.56 to 0.59.8 (using FBSDK 0.10.1), the facebook login don't work anymore on android.
when I fill the Fb login form and continue, LoginManager.logInWithPermissions promise does not resolve and never goes to .then() after logInWithPermissions()
here is my code:
loginWithFBSDKLoginManager() {
LoginManager.logOut();
const self = this;
return new Promise((resolve, reject) => {
LoginManager.logInWithPermissions(['public_profile', 'email']).then(function (result) {
if (result.isCancelled) {
return;
}
AccessToken.getCurrentAccessToken().then((data) => {
const accessToken = data.accessToken.toString();
const userID = data.userID.toString();
self
.getUserInfos(accessToken)
.then((response) => {
resolve({ ...response, accessToken, userID });
})
.catch(
function (error) {
reject(error);
}
);
});
});
});
}
I tried to put breakpoint almost everywhere but nothing help.
To most strange thing is that work perfrectly on iOS, this issue only occurs on Android.
I tried to debug my app using Android Studio and the only error found in the console is
I/chromium: [INFO:CONSOLE(0)] "Refused to display
'https://m.facebook.com/intern/common/referer_frame.php' in a frame
because it set 'X-Frame-Options' to 'deny'.", source:
https://m.facebook.com/v3.3/dialog/oauth?client_id=2129868160675609&e2e=%7B%22init%22%3A1562743341374%7D&sdk=android-5.0.3&scope=public_profile%2Cemail&state=%7B%220_auth_logger_id%22%3A%22edb48b96-de45-47e6-8331-f3db300e4eb2%22%2C%223_method%22%3A%22web_view%22%7D&default_audience=friends&redirect_uri=fbconnect%3A%2F%2Fsuccess&auth_type=rerequest&display=touch&response_type=token%2Csigned_request&return_scopes=true&ret=login&fbapp_pres=0&logger_id=edb48b96-de45-47e6-8331-f3db300e4eb2#= (0) I/chromium: [INFO:CONSOLE(53)] "ErrorUtils caught an error:
"Script error.". Subsequent errors won't be logged; see
https://fburl.com/debugjs.", source:
https://static.xx.fbcdn.net/rsrc.php/v3iEpX4/ys/l/fr_FR/LDgA15LzuMu.js
(53) I/chromium: [INFO:CONSOLE(262)] "Uncaught SecurityError: Blocked
a frame with origin "https://m.facebook.com" from accessing a frame
with origin "null". The frame requesting access has a protocol of
"https", the frame being accessed has a protocol of "data". Protocols
must match.
", source: https://static.xx.fbcdn.net/rsrc.php/v3iEpX4/ys/l/fr_FR/LDgA15LzuMu.js
(262)
Could somebody help me solving this? or guide me to find the root cause.
Thanks
I manageed to solve it by upgrading from v0.10.1 to v1.0.1. Remember to remove all the CallbackManager stuff in MainApplication.java
I'm working on a web project (HTML, CSS, JavaScript, with back-end in PHP). I've successfully got a Google Sign-in working, using their simple API, but can't get the Microsoft equivalent to function. The official online solutions to this seem to rely on .NET or PHP Composer. I'll try composer if that's the only way but a pure JS/PHP method would be easiest.
I've tried to use the following:
https://github.com/microsoftgraph/msgraph-sdk-javascript
https://github.com/AzureAD/microsoft-authentication-library-for-js
The code below is the closest I've come to a working solution. I can get some kind of user ID (which appears to be unique and constant for each user). This might be enough to set up the login system I want, but it would be ideal if I could also fetch their name and profile picture.
<script class="pre">
var userAgentApplication = new Msal.UserAgentApplication("MY CLIENT ID", null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
userAgentApplication.loginPopup(["user.read"]).then(function (token) {
var user = userAgentApplication.getUser(); //this is good
//user.userIdentifier seems to be a unique ID
//I will store this and use it for future verification
console.log(user);
//START
// get an access token
userAgentApplication.acquireTokenSilent(["user.read"]).then(function (token) {
console.log("ATS promise resolved");
}, function (error) {
console.log(error);
// interaction required
if (error.indexOf("interaction_required") != -1) {
userAgentApplication.acquireTokenPopup(["user.read"]).then(function (token) {
// success
console.log("s2");
}, function (error) {
console.log("e2");
// error
});
}
});
//END
// signin successful
}, function (error) {
console.log(error);
// handle error
});
</script>
(this code won't run as I've pasted it because it relies on the MSAL script from the second github link, and needs an application client ID)
After getting the access token with scope user.read , you could call microsoft graph api to get sign-in user's profile information such as displayName , businessPhones :
https://graph.microsoft.com/v1.0/me
Content-Type:application/json
Authorization:Bearer {token}
To get user's profile photo :
GET https://graph.microsoft.com/v1.0/me/photo/$value
In addition , if you are using Microsoft Graph JavaScript Client Library in first link , you could get user's displayName and profile photo by :
client
.api('/me')
.select("displayName")
.get((err, res) => {
if (err) {
console.log(err);
return;
}
console.log(res);
});
// Example of downloading the user's profile photo and displaying it in an img tag
client
.api('/me/photo/$value')
.responseType('blob')
.get((err, res, rawResponse) => {
if (err) throw err;
const url = window.URL;
const blobUrl = url.createObjectURL(rawResponse.xhr.response);
document.getElementById("profileImg").setAttribute("src", blobUrl);
});
Please refer to code sample here .
I'm working in order to leverage the usage of the AD for authentication and authorization of several applications, and I'm currently studying how to implement said process.
This is for a Web-Browser to Web-Application flow.
I create an AuthenticationContext instance and use it to sign in, and that much functions normally.
(Code organization simplified for demo purposes)
this.adal = new AuthenticationContext({
tenant: this.tenantId,
clientId: this.clientId,
redirectUri: this.redirectUri,
callback: this.loginCallback,
popUp: true
});
this.adal.login();
It is when I try to acquire a Token that the behaviour becomes fishy.
It is relevant to say that this application's registry in the AD has the permission "Sign in and read user profile" on Microsoft Graph API.
this.adal.acquireToken("https://graph.microsoft.com", function(error, token) {
console.log(error);
console.log(token);
});
The error is written to the console as follows: "Token renewal operation failed due to timeout"; whilest token is written as a null object. A brief look at the "Network" tab while inspecting the page with Chrome reveals such a resource:
authorize?response_type=token&client_id=xxxxx&resource=xxxxx&redirect_uri=http://localhost:8080(.....)
The Status for said resource is 302.
Got any clues? Thanks!
Ok.. it seems like I've figured it out, with a little help from this article click for article and this click for very cool info
I've replaced the following bit of code, in the login callback
this.adal.acquireToken("https://graph.microsoft.com", function(error, token) {
console.log(error);
console.log(token);
});
for this:
var cachedToken = this.adal.getCachedToken(client_id_goes_here);
if (cachedToken) {
this.adal.acquireToken("https://graph.microsoft.com", function(error, token) {
console.log(error);
console.log(token);
});
}
And finally just add this line of code to a function that is run after the acquireToken method redirects to the page:
this.adal.handleWindowCallback();
Hope this is helpful for others who run by this issue!