I need to implement login functionality with Microsoft via a new tab.
Currently our login process works with Microsoft in a way which is not really suited for our needs. The user clicks a login button, gets redirected to the Microsoft sign in page, enters his information and gets redirected back to our login page. This all happens on the same tab. In the code bellow is an example of this:
authenticateOffice(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => {
});
let currentUrl = location.href;
this.redirectLocationUrl = currentUrl.substr(0, currentUrl.indexOf('#'));
let url = "https://login.microsoftonline.com/".concat(this.office365Domain, "/oauth2/v2.0/authorize?client_id=", this.clientId,
"&response_type=id_token&redirect_uri=", this.redirectLocationUrl, "&response_mode=fragment&scope=openid profile&state=12345&nonce=678910");
window.location.href = url;
}
Pretty straightforward and nothing too complex.
What we need to do in layman's terms is:
1. Open a new tab when clicking the login button
2. User enters his information on the Microsoft login page
3. When authorized, tab is automatically closed
4. Our login page then receives the "id_token" from Microsoft from oauth2 and with this I do the rest of my login logic
else if (!StringHelper.isNullOrEmpty(window.location.hash) && window.location.hash.startsWith('#id_token')) {
let response = window.location.hash;
this._office365Service.getOfficeId(response)
.subscribe((result: string) => {
let key = result;
if (key) {
this._loginService.processOfficeIdResult(key);
} else {
abp.message.warn(this._localizationService.localize('Office365User', this.localizationSourceName));
}
});
}
My question is this:
How can we regain or rather take control of this "new" tab and do something with it?
How can we know when authorization is complete?If it fails or is successful?
In my previous research all i could come by was the callback approach but how can you send a function to a new tab which knows when to close the tab?And how can you receive authorization information when the user is successfully authenticated?
var win = window.open("window.html");
And then what? You write a function which calls itself every second and checks for input?
(just throwing ideas here)
Related
I am trying to build a Unity application to be deployed with WebGL. I am trying to incorporate Google Sign-In into the application, and so far, this was what I've managed to make work in the Unity WebGL build in Chrome:
User presses on the "Login with Google" button on Unity application, in Tab A.
User is directed to Google Sign In page on another Tab B.
User signs in with Google account, and is redirected to my redirect_uri, which is simply https://localhost, with the auth code parameter.
My question is, is it possible for me to do the following, possible with .jslib files:
Instead of going to redirect_uri on Tab B, instead go back to Tab A without reloading, passing along the auth code.
Building on the line above, have javascript handlers, that:
When auth code is received, initiate request to exchange auth code for the id_token as instructed here.
When id_token is received, call a C# Script function to do further actions with the id_token.
Alternatively, I can set redirect_uri to be an endpoint on my backend server, and perform the auth token -> id_token flow using the Google client SDKs. However, for this approach, I would like to know if i am able to
After the auth token -> id_token flow is completed on the backend server, close the current window, Tab B, and go back to Tab A.
After we’re back on Tab A, redirect Unity to a specific scene (not the login scene anymore, but a home page that users are directed to after they are authenticated).
Would very much appreciate any help i can get :')
EDIT: For better clarity, what I want to achieve is something that FacebookSDK for Unity has done in their FB.LogInWithReadPermissions(). The whole auth code -> access_token flow is seamless, and i get redirected back to the Unity application in Tab A at the end with the access_token.
I managed to find a Javascript solution to achieve my first method. The differences are that because
My application will never be in production
Consistency with my Facebook OAuth implementation,
I used the implicit flow instead of the authorization code flow, despite it being not the recommended way due to security concerns. However, I think you can easily use the authorization code flow, retrieving the authorization code and passing it on to your backend to exchange for an id token. (as far as I know, you cannot use Javascript/XHR requests to do this exchange)
So, the flow is that from my C# script, I call a Javascript function from a .jslib file. Basically, the function detects when the OAuth window has redirected back to my redirect_uri, then gets the access_token parameter from the redirected URI, and calls a C# Script function. From there, you should be able to do whatever you need to do (change scene, send to your backend, etc.). Note that there is a try/catch because there will be errors if you attempt to get information from the Google Sign In pages.
The file is as follows:
mergeInto(LibraryManager.library, {
OpenOAuthInExternalTab: function (url, callback) {
var urlString = Pointer_stringify(url);
var callbackString = Pointer_stringify(callback);
var child = window.open(urlString, "_blank");
var interval = setInterval(function() {
try {
// When redirected back to redirect_uri
if (child.location.hostname === location.hostname) {
clearInterval(interval) // Stop Interval
// // Auth Code Flow -- Not used due to relative complexity
// const urlParams = new URLSearchParams(child.location.search);
// const authCode = urlParams.get('code');
// console.log("Auth Code: " + authCode.toString());
// console.log("Callback: " + callbackString);
// window.unityInstance.SendMessage('Auth', callbackString, authCode);
// Implicit Flow
var fragmentString = child.location.hash.substr(1);
var fragment = {};
var fragmentItemStrings = fragmentString.split('&');
for (var i in fragmentItemStrings) {
var fragmentItem = fragmentItemStrings[i].split('=');
if (fragmentItem.length !== 2) {
continue;
}
fragment[fragmentItem[0]] = fragmentItem[1];
}
var accessToken = fragment['access_token'] || '';
console.log("access_token: " + accessToken);
child.close();
// Invoke callback function
window.unityInstance.SendMessage('Auth', callbackString, accessToken);l
}
}
catch(e) {
// Child window in another domain
console.log("Still logging in ...");
}
}, 50);
}
});
Then, in my C# script, I call this function using the following:
public class GoogleHelper : MonoBehaviour
{
[DllImport("__Internal")]
private static extern void OpenOAuthInExternalTab(string url, string callbackFunctionName);
// ...
public void Login(string callbackFunctionName) {
var redirectUri = "https://localhost";
var url = "https://accounts.google.com/o/oauth2/v2/auth"
+ $"?client_id={clientId}"
+ "&response_type=token"
+ "&scope=openid%20email%20profile"
+ $"&redirect_uri={redirectUri}";
OpenOAuthInExternalTab(url, callbackFunctionName);
}
// ...
}
Of course, this is super hacky, and I'm not very familiar with Javascript and so don't really know the implication of the code above, but it works for my use case.
I've got a site (asp.net mvc razor) on wich some functionalities require authorization / login.
These functionalities can be started by clicking on a button for example.
By clicking on such a button, the system checks whether the user is logged in or not.
If not, the user is redirected to the login page where he can sign in.
After that he will be redirected to the initial page again without initiating the users action.
So heres the workflow:
->Page x -> button y -> click -> redirect to login -> login -> redirect to x.
The redirects are simple Url.Action() statements.
What I want to do is to dynamically redirect to the page the click came from and ideally jump to the senders selector in order to simplify things for users.
What possibilities do I have to achieve this?
Only things coming to my mind are quite ugly stuff using ViewBag and strings
Update:
Info: As storing session variables causes problemes concerning concurrent requests this feature is disabled solution wide so I cannot use session variables.
Besides: One of the main problems is, that I cannot sign in without making an ajax call or sending a form. And by sending a form or making an ajax call I loose the information about the original initiator of the action and the parameters.
I solved this by adding by adding this to all such actions in their controllers:
[HttpPost]
public ActionResult ActionA(Guid articleId, Guid selectedTrainerId)
{
//if user is not authenticated then provide the possibility to do so
if (!Request.IsAuthenticated)
{
var localPath = this.ControllerContext.RequestContext.HttpContext.Request.Url?.LocalPath;
var parameter = this.ControllerContext.RequestContext.HttpContext.Request.Params["offeringRateId"];
var returnUrl = localPath + "?articleId=" + parameter;
return PartialView("LoginForOfferingPreview", new LoginForOfferingPreviewViewModel
{
RequestUrl = returnUrl,
//this will be used in the view the request was initiated by in order to repeat the intial action (after login has been successfull)
Requester = OfferingPreviewRequester.CourseTrialAdd,
//this will be used in the view to initiate the request again
RequestParameters = new List<dynamic> { new { articleId = articleId },new { selectedTrainerId = selectedTrainerId }}
});
}
//actual action
SendBasketEvent(new CourseAddMessage
{
BasketId = BasketId,
OfferingRateId = articleId,
SelectedTrainerId = selectedTrainerId,
SelectedTime = selectedTime,
Participants = selectedParticipants,
CurrentDateTime = SlDateTime.CurrentDateTimeUtc(SlConst.DefaultTimeZoneTzdb),
ConnectionId = connectionId
}, connectionId);
return Json(JsonResponseFactory.SuccessResponse());
}
the hereby returned view for login contains following js code that is called if the login has been succesfull:
function onLoginFormSubmit(data) {
//serialize form containing username+pw
var datastring = $("#loginForm").serialize();
$.ajax({
type: "POST",
url: '#Url.Action("Login_Ajax","Account",new {area=""})',
data: datastring,
success: function (data) {
debugger;
// display model errors if sign in failed
if (!!!data.Success) {
$(".buttons-wrap").append('<span id="loginFormError" style="color:red;"></span>');
$("#loginFormError").append(data.ErrorMessage);
}
//call method of initiating view that will decide what to dow now
if (data.Success) {
var parametersObjectAsString = JSON.parse('#Html.Raw(JsonConvert.SerializeObject(Model.RequestParameters))');
window.onLoginForOfferingPreviewSuccess('#Model.RequestUrl', parametersObjectAsString, '#((int)Model.Requester)');;
}
},
error: function () {
}
});
}
this works fine as long sigining does not fail due to wrong username or pw.
If that happens, the view shows the errors but by now signing in again somethign really strange happens:
At first it seems to work exaclty like signing in successfully by the first time but then the ajax calls in window function onLoginForOfferingPreviewSuccess will always reach the error block without beeing able to tell you why.
Fiddler reveals weird http resonse codes like 227,556 or something
Thx
I am using LinkedIn Javascript SDK to log my users in, and I need to detect if a user closes the login/auth window before they complete the login or authorization. Current SDK doesn't fire the login callback when the window is closed (I naturally expect it to be called with IN.User.isAuthorized() set to false just like in Facebook Javascript SDK).
How can I detect when the user closes the Login with LinkedIn window?
The LinkedIn API is a bit of a nightmare to deal with.
I had a similar issue where it was firing multiple requests if they opened the auth window more than once. I solved this by adding a count each time they opened the window and then ignoring everything if count > 1. My solution involves Angular and Promises so I'm not going to post the full solution.
For you, I would just add authTriggered and authComplete variables. The triggered gets set when they click they link/button to authorise with LinkedIn and the complete variable gets set in the auth callback.
Something like this perhaps?
var LinkedIn = LinkedIn || {};
LinkedIn = {
authTriggered: false,
authComplete: false,
authorise: function() {
IN.User.authorize(function() {
this.authComplete = true;
});
}
};
var authLink = document.getElementById('auth-link');
authLink.addEventListener('click', function(e) {
e.preventDefault();
LinkedIn.authTriggered = true;
LinkedIn.authorise();
});
Instead of IN.User.authorize() please use IN.UI.Authorize() as
var linkedin = IN.UI.Authorize().place();
linkedin.onWindowRemove.subscribe(function() {
// perform some action
});
thanks sanju for this answer
https://sanjutalks.wordpress.com/2017/10/04/linkedin-javascript-sdk-detecting-login-windows-close-event/
I am working on playframework but I believe this question is more about a general topic of web implementation. I am creating a website where I want to put my signin button on the top right corner of my home page and would like to update it based on user authentication.
i.e. if user is logged in there would be my profile and logout button and if not then there would be only signin button. I know how to implement it using different pages that uses different routes, in this case I can load complete page but I don't want to load complete page instead I would like to use popup window for signin/signup and want user to redirect back on the same page after signing in (click on signin -> signin form as a popup -> submit -> signed in) url shouldn't be changing in this process. I have seen this type of design in many popular websites but I don't know how to build one.
I did some research and found, we can do this using jquery's ajax call. With the help of ajax call we can request data from server in background (here I will request html) and update my current page DOM. In this case I am supposed to update DOM of my navbar's top right corner so I will request html for that part only but I don't know exactly how to do it? I am new to website designing, would it be a good design or there is other best way to do the same task?
It would be also appreciable if anyone can tell me how should I update link to my related css & js file page by page. I mean if some css file is not being used in a particular page how to remove reference to that and add a new one relevant to that page.
Sorry, If it looks fool of asking such basic questions here but I just want to clear my concept in web-designing and implementation. It would also be helpful if anyone can suggest me a book or link to read about these topics.
If I understood your question correctly, you were asking for help with the DOM -part of the equation?
Here's an example (see the JSFiddle, for the whole thing, as the current code expects the login button to be present at launch):
function login (logtype) {
var insertInput = function (value) {
document.getElementById('logincontainer').insertBefore(
document.createElement('input'),
document.getElementsByTagName('input')[0]);
var newButton = document.getElementsByTagName('input')[0];
newButton.type = 'button';
newButton.value = value;
if (value === 'logout') {
newButton.onclick = function () {login('logout');};
}
if (value === 'login') {
newButton.onclick = function () {login('login');};
}
if (value === 'myprofile') {
newButton.onclick = function () {window.location.href='http://jsfiddle.net/user/b00t/fiddles/';};
}
},
deleteInput = function () {
document.getElementById('logincontainer').removeChild(document.getElementById('logincontainer').lastChild);
};
if (logtype === 'login') {
var logIn = confirm('Login?');
if (logIn) {
insertInput('myprofile');
insertInput('logout');
deleteInput();
}
}
else if (logtype === 'logout') {
var logOut = confirm('Logout?');
if (logOut) {
insertInput('login');
deleteInput();
deleteInput();
}
}
}
JSFiddle
Trying to get Facebook to authenticate my users via a javascript popup. Right now, I have:
<input type="button" value="Connect with Facebook" onclick="window.open('https://graph.facebook.com/oauth/authorize?client_id=XXXXXXXXXXX&redirect_uri=http://example.com/step2&display=popup')" />
But when the user logs in via Facebook, the popup just displays the Facebook.com homepage. I'd like for the popup to authenticate the user and go away so that I can start retrieving user data from the graph api.
Is there a better / easier way to do this? Simple examples are appreciated.
Thank you.
oauth2 in facebook involves two steps, call authorize to get code, then call access_token to get token.
One way to deal with the pop login:
open login url in new window just like you did,when the facebook redirects back to your url in the popup, you set the cookie either through server side code or using javascript to capture url query parameter, when page is loaded in the popup, close the window immediately window.close.
On your main page, after your window.open code, add JavaScript code to detect if popup is closed and capture the cookie:
var signinWin;
$('#FacebookBtn').click(function () {
var pos = screenCenterPos(800, 500);
signinWin = window.open("[URL]", "SignIn", "width=780,height=410,toolbar=0,scrollbars=0,status=0,resizable=0,location=0,menuBar=0,left=" + pos.x + ",top=" + pos.y);
setTimeout(CheckLoginStatus, 2000);
signinWin.focus();
return false;
});
function CheckLoginStatus() {
if (signinWin.closed) {
$('#UserInfo').text($.cookie("some_cookie"));
}
else setTimeout(CheckLoginStatus, 1000);
}
Why not simply...
function authorizeAppInPopup() {
FB.login(function(response) {
if (response.authResponse) {
// User authorized app
} else {
// User cancelled login or did not fully authorize
}
}, {scope: 'publish_stream'});
}
??? : ]
https://developers.facebook.com/docs/reference/javascript/FB.login/
Checkout this article: Create Facebook PopUp Authentication Window using PHP and javascript for customize popup authentication.
It might be a good idea to do both a callback function from the Child window as Avner says as well as a timer that watches for the window to be closed. That way if the Child window is closed without a specific action you can take appropriate action on the Parent window.
**On Child**
// Set oAuthToken from server side when it comes back from authenticating
// and you have the token on the server side.
var oAuthToken = "";
oAuthToken = "--STRING INSERTED BY SERVER SIDE CODE--";
window.opener.pbFromPopup(oAuthToken);
**On Parent :**
function CheckLoginStatus() {
if (authWindow.closed) {
// Handle error if authentication window is closed
// without any action on Allow or Deny
alert("window closed");
//location.href = "errorPage.aspx?error=authwinclosed;
}
else setTimeout(CheckLoginStatus, 1000);
}
function pbFromPopup(token) {
// Function called from child window,
// token is passed back from child
authWindow.close();
// Put token in a hidden form field and submit the form to pass
// it back to the server
$("#authToken").val(token);
$("#form1").submit();
}