Using MSAL v0.1.3 with Aurelia for Authentication - aurelia

I've found all kind of examples of using MSAL and even examples of using MSAL with SPA applications (generally Angular or React) but I'm struggling to get this to work with Aurelia.
I did the following to ensure that the library is actually working as expected.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/0.1.3/js/msal.min.js"></script>
<style>
.hidden {
visibility: hidden;
}
</style>
</head>
<body>
<div id="username"></div>
<div id="token"></div>
<button id="login" onclick="loginPopup()">Log In</button>
<button id="logout" class="hidden">Log Out</button>
<script>
var authClient = new Msal.UserAgentApplication("my v2 endpoint client id",null);
function loginPopup(){
authClient.loginPopup(["openid","user.readbasic.all"])
.then(token => {
authClient.acquireTokenSilent(["user.readbasic.all"])
.then(accessToken => {
updateUI(token);
},
error => {
authClient.acquireTokenPopup(["user.readbasic.all"])
.then(accessToken => {
updateUI(token);
},
error => {
console.log("Token Error: " + error)
})
})
},
error =>{
console.log("Login error " + error)
})
}
function updateUI(token){
var loginbutton = document.getElementById("login");
loginbutton.className = "hidden";
let usernamediv = document.getElementById("username");
let tokendiv = document.getElementById("token");
usernamediv.innerText = authClient.getUser().name;
tokendiv.innerText = token;
}
</script>
</body>
</html>
That code works great. You click the Log In button, the Login Popup is displayed, you select the user, enter your password and the popup disappears and the UI is updated appropriately with username and token.
Now I'm trying to add this into my Aurelia app as follows:
main.js
export async function configure(aurelia){
aurelia.use
.standardConfiguration()
.developmentLogging();
let msClient = new Msal.UserAgentApplication('my v2 endpoint client id",null);
aurelia.use.instance("AuthService",msClient);
await aurelia.start();
if(msClient.getUser()) {
await aurelia.setRoot(PLATFORM.moduleName("app"));
else
await aurelia.setRoot(PLATFORM.moduleName("login/login"));
login.js
export class Login {
static inject = [Aurelia, "AuthService"]
constructor(aurelia, auth){
this.authService = auth
this.app = aurelia
}
activate(){
//Also tried this code in constructor
this.authService.loginPopup(["openid","user.readbasic.all"])
.then(token => {
this.app.setRoot(PLATFORM.moduleName("app"))
});
}
}
However, with this code in place, the app loads and navigates to the login page which pops up the login popup. However, once the user enters/selects name and password, the popup screen does not go away. The app (behind the popup) seems to reload and navigate to the app.js viewmodel but the popup remains on the screen and appears to be asking the user to enter/select username.
I've also tried using loginRedirect rather than loginPopup with a similar result in that the app is constantly redirected to the login page even after authenticating.
I'm guessing this has to do with the way MSAL is trying to respond to the app and the way Aurelia is trying to handle navigation, etc but I can't figure out where things are going awry.
Any help would be greatly appreciated!

Use the attached() lifecycle callback
It seems like everything is working, but the popup isn't going away. It's hard to know why without seeing the code in action, but the first thing you'll want to try is moving the relevant code to the attached() callback, which is called once the view and view-model are bound and added to the DOM.
Do NOT return the login promise in activate()
If you returned the login promise in activate(), activation wouldn't complete until the promise resolved. However, app.setRoot() is being called when the promise resolves as well, which means that you would be starting a new navigation before the first one completed. This is bad.
activate() {
// THIS WOULD BREAK
return this.authService.loginPopup(["openid","user.readbasic.all"])
.then(token => {
this.app.setRoot(PLATFORM.moduleName("app"))
});
}
Use the detatched() lifecycle callback
If that doesn't work, the next thing to do is check to see if authService.loginPopup() is properly logging in and resolving. If so, you can always go ahead and manually remove the popup from the DOM in the case that the library wasn't cleaning up after itself. You should do this detatched() lifecycle callback.

Related

how to get data from local google Auth?

I am facing a problem in findding a login data of user and user is logedin to my site using google auth platform and i want to get that login data and store data in my local storage and I am working on Angular 14
Kindly help if any one know the soluiton of this problem
Thanks
I had searched a lot but not find a convieniet solution
It's work for me in this way.
According to the new documentation of Google (https://developers.google.com/identity/gsi/web/guides/overview), you should follow next steps:
Create a google app in google cloud console platform and generate a client id.
Load the client library. Add this script "<script src="https://accounts.google.com/gsi/client" async defer>" between the <head></head> tags of your index.html file of Angular project.
Add this code on ngOnInit() function in the component that you would like to have "Sign in with Google button."
ngOnInit() {
// #ts-ignore
google.accounts.id.initialize({
client_id: "YOUR GOOGLE CLIENT ID",
callback: this.handleCredentialResponse.bind(this),
auto_select: false,
cancel_on_tap_outside: true,
});
// #ts-ignore
google.accounts.id.renderButton(
// #ts-ignore
document.getElementById("google-button"),
{ theme: "outline", size: "large", width: "100%" }
);
// #ts-ignore
google.accounts.id.prompt((notification: PromptMomentNotification) => {});
}
async handleCredentialResponse(response: any) {
// Here will be your response from Google.
console.log(response);
}
Add div or button element to the html file of this component, with the same id that you mentioned into the initialization. ( "google-button" ):
<div class="" id="google-button"></div>.
Let me know if you have any issues.

How to dispay flash message after redirect vue-router

I'm creating simple login logout application using vuejs and express. After user register successfully, I redirect them to page login by this.$router.push('/login'), and I want to display a flash message like 'Register sucess. Now log in!'
Here is my method
In Register.vue
this.$router.push("/login", () => {
console.log('Register success')
EventBus.$emit('registerSuccess')
});
In Login.vue
<div class="alert alert-success" role="alert" v-if="flashMsg">{{flashMsg}}</div>
data() {return {flashMsg: ''}}
created() {
console.log('Created component')
EventBus.$on("registerSuccess", () => {
console.log('Set flash msg')
this.flashMsg = "Now log in!"
});
},
It don't work. EventBus listen the emit but don't set any value to flashMsg.
Console
Register success
Set flash msg
Created component
Can someone tell me how to do this? Thank you
According to this, what you are looking for is not possible because you are changing routes before emitting an event and therefore it cannot be caught.
I suggest you try using localStorage/SessionStorage, Vuex or query strings.

PDFTron full api is not getting loaded in WebViewer

I am trying to get the full api of PDFTron working from the WEbViewer. I followed the steps in the link below.
https://www.pdftron.com/documentation/web/guides/full-api/setup/
But I am getting the error given below in console while loading the webviewer.
Uncaught (in promise) Error: Full version of PDFNetJS has not been loaded. Please pass the "fullAPI: true" option in your WebViewer constructor to use the PDFNet APIs.
at Object.get (CoreControls.js:1694)
at z.docViewer.on ((index):43)
at CoreControls.js:398
at Array.forEach (<anonymous>)
at z.O (CoreControls.js:398)
at CoreControls.js:213
This is my code.
WebViewer({
path: 'WebViewer-6.0.2/lib', // path to the PDFTron 'lib' folder on your server
type: 'html5',
initialDoc: 'forms/local.pdf', // You can also use documents on your server
fullAPI: true,
}, document.getElementById('viewer'))
.then(instance => {
const docViewer = instance.docViewer;
const annotManager = instance.annotManager;
const Annotations = instance.Annotations;
Annotations.ChoiceWidgetAnnotation.FORCE_SELECT=true;
const Actions = instance.Actions;
docViewer.on('documentLoaded', async () => {
const PDFNet = instance.PDFNet;
await PDFNet.Initialize();
// This part requires the full API: https://www.pdftron.com/documentation/web/guides/full-api/setup/
alert('async');
const doc = docViewer.getDocument();
// Get document from worker
const pdfDoc = await doc.getPDFDoc();
pdfDoc.getAcroForm().putBool("NeedAppearances", true);
});
docViewer.on('documentLoaded', () => {
docViewer.on('annotationsLoaded', () => {
const annotations = annotManager.getAnnotationsList();
annotations.forEach(annot => {
console.log('fieldName => '+annot.fieldName);
});
});
Please help me resolve this.
EDIT
Modified the code as suggested by #Andy.
The updated code in index.html file looks like below,
<!DOCTYPE html>
<html>
<head>
<title>Basic WebViewer</title>
</head>
<!-- Import WebViewer as a script tag -->
<script src='WebViewer-6.0.2/lib/webviewer.min.js'></script>
<body>
<div id='viewer' style='width: 1024px; height: 600px; margin: 0 auto;'>
<script>
WebViewer({
path: 'WebViewer-6.0.2/lib', // path to the PDFTron 'lib' folder on your server
type: 'html5',
fullAPI: true,
// licenseKey: 'Insert commercial license key here after purchase',
}, document.getElementById('viewer'))
.then(async instance => {
const { Annotations, Tools, CoreControls, PDFNet, PartRetrievers, docViewer, annotManager } = instance;
await PDFNet.Initialize();
Annotations.ChoiceWidgetAnnotation.FORCE_SELECT=true;
const Actions = instance.Actions;
docViewer.on('documentLoaded', async () => {
// This part requires the full API: https://www.pdftron.com/documentation/web/guides/full-api/setup/
const doc = docViewer.getDocument();
// Get document from worker
const pdfDoc = await doc.getPDFDoc();
const acroFrom = await pdfDoc.getAcroForm();
acroform.putBool("NeedAppearances", true);
});
instance.loadDocument('forms/test.pdf');
});
</script>
</div>
</body>
</html>
I am loading the file from a http server in my project folder.
http-server -a localhost -p 7080
Unfortunately, I am getting the same error.
Error: Full version of PDFNetJS has not been loaded. Please pass the "fullAPI: true" option in your WebViewer constructor to use the PDFNet APIs.
We are currently evaluating PDFTron, so the licenseKey option is not passed in the WebViewer constructor.
Kindly help me on this.
I have tried out the code you have provided and was still not able to reproduce the issue you are encountering. I do typically perform the initialize outside WebViewer events so the initialization occurs only once:
WebViewer(...)
.then(instance => {
const { Annotations, Tools, CoreControls, PDFNet, PartRetrievers, docViewer } = instance;
const annotManager = docViewer.getAnnotationManager();
await PDFNet.initialize(); // Only needs to be initialized once
docViewer.on('documentLoaded', ...);
docViewer.on('annotationsLoaded', ...);
});
Also, I noticed that you attach an an event handler to annotationsLoaded every time documentLoaded is triggered. I am not sure if that is intentional or desirable but this can lead to the handler triggering multiple times (when switching documents).
This may not matter but instead of using initialDoc, you could try instance.loadDocument after the initialize instead.
await PDFNet.initialize();
docViewer.on('documentLoaded', ...);
docViewer.on('annotationsLoaded', ...);
instance.loadDocument('http://...');
There is one last thing to mention about the full API. The APIs will return a promise most of the time as the result so you will have to await the return value most of the time.
const acroFrom = await pdfDoc.getAcroForm();
// You can await this too. Especially if you need a reference to the new bool object that was
acroform.putBool("NeedAppearances", true);
Let me know if this helps!

Google SignIn2 won't call the onsuccess callback

I put a google signin button on my page using the "gapi.signin2.render" function. To this function I pass an object with the onsuccess handler.
When the user initially clicks on the button everything works as expected. The google signin popup appears. But after finishing the signin process nothing happens on my page.
After refreshing the page, the button triggers the auth process again. And as soon as the "Signed In" appears, the onsuccess handler gets called.
I looked at the network traffic and saw a response containing all the information. That means that everything should be configured correctly in the google console. Otherwise I wouldn't get the response, right?
This is my vue component:
<template>
...
<div id="google-signin-button" class="g-signin2"></div>
...
</template>
<script>
...
window.gapi.signin2.render('google-signin-button', {
onsuccess: this.onSignIn,
})
...
onSignIn: function(googleUser) { }
<script>
This is the index.html:
<meta name="google-signin-client_id" content="xxx.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js" async defer></script>
What can I do to get the onsuccess handler called on the initial signin?
Ok, i found it. Unfortunately i misunderstood the official documentation.
I had to load the auth2 module first:
window.gapi.load('auth2', () => {
window.gapi.signin2.render('google-signin-button', {
onsuccess: this.onSignIn,
})
})

Facebook JS SDK FB.Login function

In my website, I have a button for "facebook connect", which when clicked on, triggers the following code:
<script type="text/javascript">
$(document).ready(function(){
console.log(<?=(isLoggedIn()+0)?>);
$("#fbconnect").click( function(){
FB.login(function(response) {
alert(JSON.stringify(response.authResponse));
if (response.authResponse) {
window.location = window.location;
alert('Welcome! Fetching your information.... ');
} else {
alert('User cancelled login or did not fully authorize.');
}
}, {scope: 'email,user_likes'});
} );
});
</script>
The button works well: it shows the facebook login dialog, and then alerts the appropriate message. However, when I refresh the page, the user is still logged out both server-side and client-side. Do I need to handle the access token and cookies myself or does the javascript SDK take care of that? If the latter, then why doesn't this code work?
Thank you.
UPDATE 1:
It works perfectly on localhost with Firefox, and on the server with all browsers. So the problem exists only for chrome and on localhost. Is there some chrome security setting that prevents cookies from being saved for localhost?