Logout is not clearing all process using oidc client with identity server 4 - single-page-application

We are using Office.context.ui.displayDialogAsync for authentication with OAUTH library (Oidc-client) and below are the findings. Kindly help on the same.
As per attached code we were able to get access token in taskpane.ts file as args in messageHandler...
But when i logged in fresh browser that time only Secure Token Service (STS) login window getting opening.
If i logged out and cleared access token then again trying to logged in that time directly getting in as logged user without opening Secure Token Service (STS) window.
Once i cleared browser cache and all then only i am able to get Secure Token Service (STS) window again... Can you please advise about the scenario to handle? Do we need anything.
Current Scenario
displayDialogAsync getting opened as STS login very first time and able to login successfully. But for the subsequent login it is not getting popup and directly loading the data with tokens.
Expected Scenario
displayDialogAsync should not only open in first time login but also it should open for subsequent login which means if user logged out and trying to login again that time also it should popup.Is there anything need to clear cache for displayDialogAsync? Kindly help.
auth.ts
Office.initialize = function () {
var settings = {
authority: "https://xxxxxx.com/xxxx/xx",
client_id: "https://xxxxxxx.com/",
redirect_uri: "https://localhost:3000/taskpane.html",
// silent_redirect_uri:"https://localhost:3000/taskpane.html",
post_logout_redirect_uri: "https://xxxxxxx.com/",
response_type: "id_token token",
scope: "openid read:xxxx read:xxxxxx read:xxxxxxx",
state: true,
clearHashAfterLogin: false,
filterProtocolClaims: true,
loadUserInfo: true,
nonce:true,
};
Oidc.Log.logger = console;
var mgr = new Oidc.UserManager(settings);
mgr.signinRedirect();
mgr.signinRedirectCallback().then((user) => {
if (user) {
console.log(user);
} else {
mgr.signinPopupCallback().then(function (user) {
window.location.href = '../';
}).catch(function (err) {
console.log(err);
});
throw new Error('user is not logged in');
}
});
};
taskpane.ts
const loginpopup = function () {
if (OfficeHelpers.Authenticator.isAuthDialog())
return;
Office.context.ui.displayDialogAsync(
url,
{ height: 60, width: 60, /*displayInIframe:true*/ },
dialogCallback);
function dialogCallback(asyncResult) {
if (asyncResult.status == "failed") {
switch (asyncResult.error.code) {
case 12004:
console.log("Domain is not trusted");
break;
case 12005:
console.log("HTTPS is required");
break;
case 12007:
console.log("A dialog is already opened.");
break;
default:
console.log(asyncResult.error.message);
break;
}
}
else {
dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, messageHandler);
}
}
function messageHandler(arg: any) {
if (arg != "jsonMessage") {
$(".loader").show();
var test = JSON.parse(arg.message).value.split("#")[1].split("&")[1].split("=");
dialog.close();
};
}
}
logout.ts
Office.initialize = () => {
var settings = {
authority: "https://xxxxxx.com/xxxxxx/v1",
client_id: "https://xxxxxxx.com/",
redirect_uri: "https://localhost:3000/logout.html",
post_logout_redirect_uri: "https://localhost:3000/logout.html",
metadata: {
issuer: 'https://xxxxxx.com/xxxxxx/v1',
authorization_endpoint: "https://xxxxxx.com/xxxxxxx/v1/xxxxx"
}
};
var mgr = new Oidc.UserManager(settings);
mgr.signoutRedirect();
mgr.removeUser();
mgr.revokeAccessToken();
mgr.clearStaleState();
$("document").ready(function () {
localStorage.removeItem('accessToken');
localStorage.clear();
});

Related

Firebase Phone Auth Error 400

Just getting started with Firebase phone auth. Seems pretty slick however I've hit a wall with a bug.
{
"error": {
"errors": [
{
"domain": "global",
"reason": "invalid",
"message": "SESSION_EXPIRED"
}
],
"code": 400,
"message": "SESSION_EXPIRED"
}
}
Starting with the Captcha: (standard documentation code!)
var applicationVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
'size': 'invisible',
'callback': function(response) {
},
'expired-callback': function() {
}
});
Its rendered and the captcha works well.
Next is the sign-in bit where you are sent the auth code to your phone. Works great:
$scope.signInWithPhoneNumber = function signInWithPhoneNumber() {
var phoneNumber = "*censored*";
var appVerifier = window.recaptchaVerifier;
firebase.auth().signInWithPhoneNumber(phoneNumber, applicationVerifier)
.then(function (confirmationResult) {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
$scope.setConfirmationResult(confirmationResult);
alert('Result: ' + JSON.stringify(confirmationResult));
}).catch(function (error) {
// Error; SMS not sent
alert('Error: ' + error);
// ...
});
};
Finally its the authentication of the code that the user inputs from the text message. Here is when I get the error 400:
$scope.AuthenticateCode = function (code) {
var code = String(document.getElementById("auth_code").value);
var confirmationResult = $scope.getConfirmationResult();
alert(code);
confirmationResult.confirm(code).then(function (result) {
// User signed in successfully.
var user = result.user;
console.log('Signed In! ' + JSON.stringify(user));
// ...
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
}//end of AuthenticateCode
The error is coming from the VerifyPhone method:
https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key=censored
Any help or ideas?
Many Thanks,
Kieran
Ok, there are 2 likely reasons:
The code expired. The user took too long to provide the SMS code and finish sign in.
The code was already successfully used. I think this is the likely reason. You need to get a new verificationId in that case. Get a new reCAPTCHA token via the invisible reCAPTCHA you are using.
You are most likely to forget the "Country Code" before the phone no.
That is why firebase throw error 400 which means invalid parameters
If it's an Ionic3 project, change the following lines:
Imports:
import { AngularFireAuth } from 'angularfire2/auth';
import firebase from 'firebase';
Create var:
public recaptchaVerifier: firebase.auth.RecaptchaVerifier;
on "ionViewDidLoad()"
this.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
on "your_method(phoneNumber: number)"
const appVerifier = this.recaptchaVerifier;
const phoneNumberString = "+" + phoneNumber;
this.fireAuth.auth.signInWithPhoneNumber(phoneNumberString, appVerifier)
.then(confirmationResult => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
let prompt = this.alertCtrl.create({
title: 'Enter the Confirmation code',
inputs: [{ name: 'confirmationCode', placeholder: 'Confirmation Code' }],
buttons: [
{
text: 'Cancel',
handler: data => { console.log('Cancel clicked'); }
},
{
text: 'Send',
handler: data => {
confirmationResult.confirm(data.confirmationCode)
.then(result => {
// Phone number confirmed
}).catch(error => {
// Invalid
console.log(error);
});
}
}
]
});
prompt.present();
})
.catch(error => {
console.error("SMS not sent", error);
});
Reference:
Firebase Phone Number Authentication
I got into a similar situation when a POST request to google API was returning Bad Request 400. When the message was logged, it said:
All requests from this device are blocked due to Unusual Activity. Please try again later
The issue was when the ReCaptcha was sensing a bot out of my development environment and it worked well when I tried later. During the rest of the development, I turned off this feature for easy work.

ServiceWorker WindowClient.navigate promise rejected

I'm using Firebase Cloud Messaging + Service worker to handle background push notifications.
When the notification (which contains some data + a URL) is clicked, I want to either:
Focus the window if it's already on the desired URL
Navigate to the URL and focus it if there is already an active tab open
Open a new window to the URL if neither of the above conditions are met
Points 1 and 3 work with the below SW code.
For some reason point #2 isn't working. The client.navigate() promise is being rejected with:
Uncaught (in promise) TypeError: Cannot navigate to URL: http://localhost:4200/tasks/-KMcCHZdQ2YKCgTA4ddd
I thought it might be due to a lack of https, but from my reading it appears as though localhost is whitelisted while developing with SW.
firebase-messaging-sw.js:
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'XXXX'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(payload => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
let notificationData = JSON.parse(payload.data.notification);
const notificationOptions = {
body: notificationData.body,
data: {
clickUrl: notificationData.clickUrl
}
};
return self.registration.showNotification(notificationData.title,
notificationOptions);
});
self.addEventListener('notificationclick', event => {
console.log('[firebase-messaging-sw.js] Notification OnClick: ', event);
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.notification.close();
let validUrls = /localhost:4200/;
let newUrl = event.notification.data.clickUrl || '';
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
event.waitUntil(
clients.matchAll({
includeUncontrolled: true,
type: 'window'
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
let client = windowClients[i];
if (validUrls.test(client.url) && 'focus' in client) {
if (endsWith(client.url, newUrl)) {
console.log('URL already open, focusing.');
return client.focus();
} else {
console.log('Navigate to URL and focus', client.url, newUrl);
return client.navigate(newUrl).then(client => client.focus());
}
}
}
if (clients.openWindow) {
console.log('Opening new window', newUrl);
return clients.openWindow(newUrl);
}
})
);
});
The vast majority of my SW code is taken from:
https://gist.github.com/vibgy/0c5f51a8c5756a5c408da214da5aa7b0
I'd recommend leaving out includeUncontrolled: true from your clients.matchAll().
The WindowClient that you're acting on might not have the current service worker as its active service worker. As per item 4 in the specification for WindowClient.navigate():
If the context object’s associated service worker client’s active
service worker is not the context object’s relevant global object’s
service worker, return a promise rejected with a TypeError.
If you can reproduce the issue when you're sure the client is currently controlled by the service worker, then there might be something else going on, but that's what I'd try as a first step.
This worked for me:
1- create an observable and make sure not to call the messaging API before it resolves.
2- register the service worker yourself, and check first if its already registered
3- call event.waitUntil(clients.claim()); in your service worker
private isMessagingInitialized$: Subject<void>;
constructor(private firebaseApp: firebase.app.App) {
navigator.serviceWorker.getRegistration('/').then(registration => {
if (registration) {
// optionally update your service worker to the latest firebase-messaging-sw.js
registration.update().then(() => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
});
}
else {
navigator.serviceWorker.register('firebase-messaging-sw.js', { scope:'/'}).then(
registration => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
}
);
}
});
this.isMessagingInitialized$.subscribe(
() => {
firebase.messaging(this.firebaseApp).usePublicVapidKey('Your public api key');
firebase.messaging(this.firebaseApp).onTokenRefresh(() => {
this.getToken().subscribe((token: string) => {
})
});
firebase.messaging(this.firebaseApp).onMessage((payload: any) => {
});
}
);
}
firebase-messaging-sw.js
self.addEventListener('notificationclick', function (event) {
event.notification.close();
switch (event.action) {
case 'close': {
break;
}
default: {
event.waitUntil(clients.claim());// this
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: "window"
}).then(function (clientList) {
...
clientList[i].navigate('you url');
...
}
}
}
}

AngularFire login with google, using parameters

we are using this code to login with google:
var googleOptions = {
scope: 'email'
};
$scope.auth.$authWithOAuthPopup("google", googleOptions).then(function(authData) {
console.log(authData.google.email);
var userSigninIdentifier = authData.google.id;
console.log("userSigninIdentifier:" + userSigninIdentifier);
if ($scope.googleRef.$getRecord(userSigninIdentifier) == null) {
console.warn("new user, registering...");
$scope.register(authProvider, authData);
} else {
$scope.profileID = $scope.googleRef.$getRecord(userSigninIdentifier).profileID;
$firebase(ref.child("users").child("signin").child("google").child(userSigninIdentifier)).$update({
token: authData.token,
expires: authData.expires,
AccessToken: authData.google.accessToken
});
$firebase(ref.child("users").child("data").child($scope.profileID)).$update({
displayName: authData.google.displayName,
email: authData.google.email,
picture: authData.google.cachedUserProfile.picture
});
console.log("Logged in as:", authData.uid);
$state.go('app.home');
}
}).catch(function(error) {
console.error("Authentication failed google:", error);
});
Unfortunately, the options don't work. And since the options don't work, we can't access the users email adress. How do we use AngularFire's $authWithOAuthPopup method with parameters?
Edit: this (in a slightly different form) works perfectly with facebook login

Firebase authentication on App initilisation

This is works:
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
but this one is not:
$scope.authenticated.currentUser = user.id;
My goal here is to take to take some of the authentication variables (Email+UserID) and then use them to access a profile node ON firebase. On initialization I want the username, email, and a few other things I need for the app.
crossfitApp.controller('globalIdCtrl', ["$scope",'defautProfileData','$q', function ($scope,defautProfileData,$q) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
$scope.authenticated={
currentUser: $scope.authemail,
emailAddress: "",
settings: "",
};
var chatRef = new Firebase('https://<YOUR-FIREBASE>.firebaseio.com');
var auth = new FirebaseSimpleLogin(chatRef, function(error, user) {
if (error) {
// an error occurred while attempting login
switch(error.code) {
case 'INVALID_EMAIL':
case 'INVALID_PASSWORD':
default:
}
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
$scope.authenticated.currentUser = user.id ;//
} else {
// user is logged out
}
});
}]); //GlobaldCtrl
Most likely, you're running into a problem with Angular's HTML Compiler.
Whenever you use an event like ng-click/ng-submit/etc, Angular fires $scope.$apply(), which checks for any changes to your $scope variables and applies them to the DOM.
Since FirebaseSimpleLogin is not part of Angular's purview, it has no idea that when the callback is fired, you've updated $scope.authenticated.currentUser. This would also explain why it works when you call auth.login(), since you're probably invoking that via an ng-click event somewhere, which would fire a digest check and discover the changes.
If this is indeed the case, you can correct this issue by alerting Angular that it needs to run $apply by using $timeout:
crossfitApp.controller('globalIdCtrl', ["$scope",'defautProfileData','$q', '$timeout', function ($scope,defautProfileData,$q, $timeout) {
/* ... */
var auth = new FirebaseSimpleLogin(chatRef, function(error, user) {
if (error) {
/* ... */
} else if (user) {
$timeout(function() {
$scope.authenticated.currentUser = user.id ;//
});
} else {
// user is logged out
}
});

App authentication dialog box for facebook app

I have made an facebook app. Now i need to take user information using a pop-up permission box. If a user has authenticated the app, facebook should not open dialog box for permission but if a user comes to app first time then it must open a dialog box. What I am trying to do here is...and getting error like...
Cannot call method 'showPermissionDialog' of undefined
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
alert("1");
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
//alert(uid);
var accessToken = response.authResponse.accessToken;
jQuery("#<%= accessToken.ClientID %>").val(accessToken);
// alert(accessToken);
fqlQuerynew();
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
alert('not_authorized');
OnRequestPermission();
} else {
alert("3");
//alert('the user isnt logged in to Facebook');
}
});
};
function OnRequestPermission() {
var myPermissions = "publish_stream, manage_pages"; // permissions your app needs
FB.Connect.showPermissionDialog("email,offline_access", function (perms) {
if (!perms) {
alert("hi");
// document.location.href = 'YouNeedToAuthorize.html';
} else {
alert("buy");
document.location.href = 'homePage.html';
}
});
}
If you have just copied and pasted it from your code then I think you have added one extra closing bracket after FB.getLoginStatus '};'.
After removing that try your code. If it doesn't work then can we know when you want to check login status like after clicking some social button or while loading page.
Here's a modified version of your code, I haven't tested it and it's not complete, but should give you an idea of what to do:
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
jQuery("#<%= accessToken.ClientID %>").val(accessToken);
fqlQuerynew();
} else if (response.status === 'not_authorized') {
OnRequestPermission();
} else {
...
}
});
function OnRequestPermission() {
var myPermissions = "publish_stream, manage_pages"; // permissions your app needs
FB.login(function(response) {
if (response.status === 'connected') {
FB.api("me/permissions", checkPermissions);
}
else {
....
}
}, { scope: "email,offline_access" });
}
function checkPermissions(response) {
if (response.data && response.data.legth == 1) {
var perms = response.data[0];
// iterate over perms and check if the user has all needed permissions
}
}

Resources