Browser directly calling back-end URL instead of UI app URL - javascript

I am 3 weeks old to Angular so a little confused about how it works.
Setup: We have 1 container running 2 applications (UI and Backend) UI runs on 8080 and backend runs on 8181.
Issue: When we submit the credentials on the login page, browser is making a direct call to the back-end to check if the user exists. I traced this down by looking at the network calls in Browser's developer tools. Not sure how the framework is working but how does the browser directly call the back-end instead of the UI app. As the application will mature, back-end port won't be even open for outside communication.
Details - I am implementing a login page - It basically calls the UI app to render the login.html and once the user submits the page with user ID & password; it goes to the controller which goes makes an API call to check if the user exists in DB
Browser(ClientUIIP:8080) --> UI --> Backend(check if user exists)
I have a controller.js with below code
var serverPath = 'http://localhost:8181/backend'
//Backend API URL's
var users = '/users';
<code>
function LoginCtrl($scope, $state, $http, $rootScope){
this.cancel = $scope.$dismiss;
$scope.showLogin = true;
$scope.message = "";
this.submit = function(username, email){
$http.get(serverPath + users + "/" + username)
.success(function(response){
if(response.name === username){
$scope.currentUser = username;
window.localStorage.setItem('CurrentUser', JSON.stringify(username));
$scope.showLogin = false;
return $state.go('index.main')
}else{
$scope.message = "Login not valid, please try again";
}
});
};
this.logout = function () {
window.localStorage.setItem('CurrentUser', null);
$scope.currentUser = null;
return $state.go('index.login')
};
if(getCurrentUser() != null){
$scope.showLogin = false;
}
};
Please let me know if anybody has an idea what needs to be changed so the browser calls the UI and then UI can make the call to back-end if the user exists or not.
Thank you

Related

Google Sign-in Javascript with redirect

Hello guys i am working on a web application and im in the part of authenticating my user.
Im sorry if there is already an awsner but i couldn't find it...
I have this sample code that works but gives me a popup (what i want is redirect):
var googleUser = {};
gapi.load('auth2', function () {
// Retrieve the singleton for the GoogleAuth library and set up the client.
auth2 = gapi.auth2.init({
client_id: 'my_client_id',
cookiepolicy: 'single_host_origin',
//ux_mode: 'redirect' //tried with redirect but i don't get user on object on click handler
});
auth2.attachClickHandler(document.getElementById('loginG'), {},
function (googleUser) {
var usermail = googleUser.getBasicProfile().getEmail();
var username = googleUser.getBasicProfile().getName();
var userimage = googleUser.getBasicProfile().getImageUrl();
console.log(googleUser);
});
});
So with this code i can get the user information/token and login into the app, but this happens with a popup and i want to do it with redirect like it's done on this website: https://www.realfevr.com/en/home-page/
I tried doing it with ux_mode: redirect and it does in-fact redirect the login but I can't get the object of the user so i must be doing something wrong here...
Can someone explain me what I am doing wrong to not get the user from the redirect ?

Retrieving tokens from OAuth2 authorize endpoint in angularjs

I am trying to use IdentityServer with an Angularjs application. The Angularjs front end is meant to call the authorize endpoint when an unauthenticated user attempts to navigate to the root url of the application. The app.config has a resolve property which will generate a routeChangeError. This event triggers a redirect to the OAuth2 Authorization endpoint (IdentityServer in my case, running on Asp.Net MVC).
baseballApp.config(['$routeProvider', '$locationProvider', '$httpProvider', function($routeProvider, $locationProvider, $httpProvider) {
$routeProvider.when('/players', {
resolve: {
auth: function ($q, $rootScope) {
var deferred = $q.defer();
if ($rootScope.token) {
deferred.resolve();
} else {
deferred.reject();
}
return deferred.promise;
}
},
templateUrl: '/FrontEnd/Templates/PlayerList.html',
controller: 'PlayerListController'
});
$routeProvider.otherwise({ redirectTo: '/players' });
var spinnerFunction = function (data, headersGetter) {
if (data) {
$('#spinner').show();
return data;
}
};
$httpProvider.defaults.transformRequest.push(spinnerFunction);
$httpProvider.interceptors.push('authInterceptor');}]).run(['$rootScope', '$location', 'authenticationService', function ($rootScope, $location, authenticationService) {
//authenticationService.getBearerToken().then(function(data) {
// $rootScope.token = data;
//});
debugger;
var generateRandomString = function() {
return (Math.random().toString(16) + "000000000").substr(2, 8);
}
var nonce = generateRandomString();
var state = generateRandomString();
$rootScope.$on('$routeChangeError', function () {
window.location = 'https://localhost:44301/identity/connect/authorize?client_id=baseballStats&redirect_uri=https%3a%2f%2flocalhost%3a44300%2f&response_type=id_token+token&scope=openid+profile+roles+baseballStatsApi&nonce=' + nonce + '&state=' + state;
});}]);
As you can see I redirect to the authorize endpoint if I don't have a token. However, it's not clear to me how I can retrieve the tokens after I have signed in on the authorization endpoint.
I have specified a return url, and after sign in it does redirect to the application, but how would I set up my angular code to retrieve the token that is supposed to be generated on the Authorize endpoint and sent back to the application?
I have seen many examples of people using bearer token authentication for angular, but I have not seen anyone use the Authorize endpoint of an OAuth2 server with Angular. In MVC, there are many built in callbacks to retrieve the token. I am looking for some ideas on how to implement this in angular.
Could you please give me a hint as to what I'm doing wrong?
EDIT
I have also tried to pass a hash navigation tag in the redirect uri, but get an "Error unknown client" from IdentityServer. Please see the code below
$rootScope.$on('$routeChangeError', function () {
var returnUrl = encodeURIComponent('https://localhost:44300/#/players');
window.location = 'https://localhost:44301/identity/connect/authorize?client_id=baseballStats&redirect_uri=' + returnUrl + '&response_type=id_token+token&scope=openid+profile+roles+baseballStatsApi&nonce=' + nonce + '&state=' + state;
});
Anybody know why?
I presume you're using the implicit flow grant for your spa js app?
In that case, the identity server, after you've logged in, will send the access token back to a call back url of your spa app. Using that call back page you are able to capture to token in order to pass it along to your resource server within the authorization header as a bearer token.
Checkout this video from Brock Allen, very well explained:
https://vimeo.com/131636653
By the way, you should use the oidc-token-manager, written by Brock himself. It is very easy to use and it abstracts away all communication with the identity server. It's also available on bower so you can grab it from there.
Finally I would like to point you to my own spa angular app I've been working on lately: https://github.com/GeertHuls/SecuredApi.
It actually uses the token manager in order to obtain access tokens, auto-refresh them an expose them throughout the entire angular app.
Hope this will inspire you a little in order to integrate this with angular.
I sidestepped around this issue by creating a custom ActionResult which would return a JSON response with a token when the user was already logged in via .NET Auth cookies, it's a bit hacky and looks like this;
[AllowAnonymous]
public ActionResult GetToken()
{
if (!User.Identity.IsAuthenticated)
{
Response.StatusCode = 403;
return Json(new {message = "Forbidden - user not logged in."}, JsonRequestBehavior.AllowGet);
}
var claims = new ClaimsPrincipal(User).Claims.ToArray();
var identity = new ClaimsIdentity(claims, "Bearer");
AuthenticationManager.SignIn(identity);
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var token = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
return Json(new { token }, JsonRequestBehavior.AllowGet);
}

$location.path(...) does not update page

I'm trying to move to a user's main page after successful login.
The browser's address bar is correctly updated by calling $location.path(), but the page is not reloaded.
I know it's a kind of a common problem, with several potential causes.
I think I am correctly inside angular lifecycle, since successful authentication is either triggered by an $http.post response or simulated via and angular $timeout (neither method works)
Adding $scope.apply() does not help anyway
Setting for example $location.absUrl('http://google.com') instead has no effect at all.
Here is the login controller:
var swControllers = angular.module('swControllers', []);
swControllers.controller('LoginCtrl', ['$scope', '$location', 'authentication',
function($scope, $location, authentication) {
authentication.ClearCredentials();
$scope.login = function () {
$scope.dataLoading = true;
authentication.Login($scope.email, $scope.password, function(response) {
$scope.dataLoading = false;
if (response.success) {
authentication.SetCredentials($scope.email, $scope.password);
$location.path('userHome');
//$location.absUrl('http://google.com');
//$scope = angular.element(document).scope();
//$scope.$apply();
} else {
$scope.error = response.message;
// $scope.dataLoading = false;
}
});
};
}]);
I have more general architectural concerns: is it cleaner to rely on ngRoute for this kind of stuff? Does this mean I'm limited to a single-page app?
Because I'm using Express + jade template views on the server side, and currently login page and userHome reside on two different templates.
I'm a bit dazzled by the interplay between server-side and client-side routing...

Meteor login via Third party library

I'm trying to login to my meteor site via a third party library like this one:
https://gist.github.com/gabrielhpugliese/4188927
In my server.js i have:
Meteor.methods({
facebook_login: function (fbUser, accessToken) {
var options, serviceData, userId;
serviceData = {
id: fbUser.id,
accessToken: accessToken,
email: fbUser.email
};
options = {
profile: {
name: fbUser.name
}
};
userId = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options);
return userId;
}, ......
In my client.js I have:
facebookLogin: function () {
if (Meteor.user())
return;
if (!Session.equals("deviceready", true))
return;
if (!Session.equals("meteorLoggingIn", false))
return;
// Do not run if plugin not available
if (typeof window.plugins === 'undefined')
return;
if (typeof window.plugins.facebookConnect === 'undefined')
return;
// After device ready, create a local alias
var facebookConnect = window.plugins.facebookConnect;
console.log('Begin activity');
Session.equals("meteorLoggingIn", true);
Accounts._setLoggingIn(true);
facebookConnect.login({
permissions: ["email", "user_about_me"],
appId: "123456789012345"
}, function (result) {
console.log("FacebookConnect.login:" + JSON.stringify(result));
// Check for cancellation/error
if (result.cancelled || result.error) {
console.log("FacebookConnect.login:failedWithError:" + result.message);
Accounts._setLoggingIn(false);
Session.equals("meteorLoggingIn", false);
return;
}
var access_token = result.accessToken;
Meteor.call('facebook_login', result, access_token, function (error, user) {
Accounts._setLoggingIn(false);
Session.equals("meteorLoggingIn", false);
if (!error) {
var id = Accounts._makeClientLoggedIn(user.id, user.token);
console.log("FacebookConnect.login: Account activated " + JSON.stringify(Meteor.user()));
} else {
// Accounts._makeClientLoggedOut();
}
});
});
}, // login
facebookLogout: function () {
Meteor.logout();
// var facebookConnect = window.plugins.facebookConnect;
// facebookConnect.logout();
},
The third party library (Facebook Android SDK in my case) works fine. My problem is after the "var id = Accounts._makeClientLoggedIn(user.id, user.token);" the Meteor.user() returns Undefined. However If I do a page refresh in the browser works fine and the template renders as a logged in user.
Anyone knows how to fix the 'Undefined' on client ??
PS. On server side the users collection looks fine. The meteor token and everything else are there.
Solved. I had to add : this.setUserId(userId.id);
after userId = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options); at server.js
Meteor's client side javascript can't run fibers. Fibers allows synchronous code to be used with javascript since by design js is asynchronous. This means there are callbacks that need to be used to let you know when the task is complete.
From what it looks like Accounts._makeClientLoggedIn doesn't take a callback & unfortunately and doesn't return any data looking at its source. I can't say i've tried this myself because I can't test your code without the android sdk but have you tried using Deps.flush to do a reactive flush?
Also Meteor also has very clean and easy facbeook integration. If you simply add the facebook meteor package
meteor add accounts-facebook
You can get access to a lovely Meteor.loginWithFacebook method that can make everything reactive and your code simpler and really easy. If you need to modify it to use the Android SDK Dialog instead you can easily modify the code out as the code for the module is out there for you to hack up to your spec
Edit: If you're using an external SDK such as the java SDK/cordova plugin
Set your plugin so that it redirects to the following URL (set up for meteor.com hosting):
http://yourmeteorapp.meteor.com/_oauth/facebook?display=touch&scope=your_scope_request_params&state=state&code=yourOAuthCodeFromJava&redirect=YourAPP
So in the querystring we have:
scope= Contains your facebook scope params (for permissions)
code= Your OAuth code from the java sdk
redirect=Where to redirect to after once logged in instead of the window.close
state= A cros site forgery state value, any random value will do
This url is basically used to mimic would what be given to the REDIRECT_URI at : https://developers.facebook.com/docs/reference/dialogs/oauth/
This will redirect to meteor's OAuth helper (at https://github.com/meteor/meteor/blob/master/packages/accounts-oauth-helper/oauth_server.js)
So what would happen is you give the OAuth code from Java to meteor, it fetches the OAuth token and the user's data, then redirect the user to a URL in your app

How do I renew a Facebook user_access_token if I deal with a lot of AJAX?

Please tell me if I'm understanding correctly. (because I might not be.)
User posts something on my site. (He checked "also post to Facebook".)
Client sends an AJAX POST request to my server, and my server inserts the record in my database.
The server realizes the the facebook user access token is expired, so it sends the response back to the client, while storing the post in a session.
The client does a window.location.replace(facebook_oauth_dialog_url)
Then the user will see a sudden "flash", going to Facebook, then coming back to the website. My server picks up the new access token.
My server checks the session to see what should be posted to Facebook. And then, it uses the new access token to post that to Facebook.
Is it really this tedious? Why can't I renew the app server-side without the user going through the dialog?
My entire site is Backbone.js. That means, it's one big page. I can't jump the user back and forth between Facebook and my website like this.
The idea is to make use of the Facebook JS-SDK methods:
User check the Post To Facebook option
you check if the current user is connected to your app (using FB.getLoginStatus())
if the user is connected, you have two options:
post directly using the FB.api method or
Send the access_token to your server to complete the post process there
if the user is not connected (or not logged in to Facebook), use the FB.login() method
Here's a quick example (with a Live Demo!) for you to get started:
<!DOCTYPE html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<div id="fb-root"></div>
<script>
var fbLoaded = false;
window.fbAsyncInit = function() {
FB.init({
appId : 'YOUR_APP_ID', // App ID
//channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
fbLoaded = true;
// Additional initialization code here
};
function postForm() {
var msg = document.myForm.msg.value;
// do form validation here, e.g:
if(!msg.length) {
alert("You should enter a message!");
return false;
}
// do we need to post to Facebook?
if(document.myForm.toFB.checked) {
// is the library loaded?
if(!fbLoaded) {
alert("Facebook JS-SDK is not yet loaded. Please try again later or uncheck Post To Facebook option");
return false;
}
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
/*
* message can be posted to Facebook directly
* using the FB.api method or accessToken
* can be sent to the server and do the call
* from there
*/
myAjaxCall(msg, accessToken);
} else {
// status is either not_authorized or unknown
FB.login(function(response) {
if (response.authResponse) {
var accessToken = response.authResponse.accessToken;
myAjaxCall(msg, accessToken);
} else {
alert('User cancelled login or did not fully authorize.');
}
}, {scope: 'publish_stream'});
}
});
} else {
myAjaxCall(msg);
}
return false;
}
function myAjaxCall(m,a) {
alert("Here you make the ajax call\nMessage: " + m + "\nAccess Token: " + a);
}
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
</script>
<form id="myForm" name="myForm" action="post" onSubmit="return postForm()">
<p><label>Your Message:</label><br/><textarea name="msg"></textarea></p>
<p><label>Post to Facebook?</label><input type="checkbox" value="1" name="toFB" /></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
Before posting to the server, call FB.getLoginStatus() on the client to get the latest access token. There is no flash when using this method and no user interaction, as it simply grabs a new access token.
FB.getLoginStatus( function ( response ) {
if ( response.authResponse ) {
var accessToken = response.authResponse.accessToken;
//post to server
};
} );
I hope you known that if you have publish_stream permission you don't need access token here is documentation for publish_stream and Below is the solution for four scenarios
1.The token expires after expires time (2 hours is the default).
2.The user changes his/her password which invalidates the access token.
3.The user de-authorizes your app.
4.The user logs out of Facebook.
To ensure the best experience for your users, your app needs to be prepared to catch errors for the above scenarios. The following PHP code shows you how to handle these errors and retrieve a new access token.
When you redirect the user to the auth dialog, the user is not prompted for permissions if the user has already authorized your application. Facebook will return you a valid access token without any user facing dialog. However if the user has de-authorized your application then the user will need to re-authorize your application for you to get the access_token.
<?php
$app_id = "YOUR_APP_ID";
$app_secret = "YOUR_APP_SECRET";
$my_url = "YOUR_POST_LOGIN_URL";
// known valid access token stored in a database
$access_token = "YOUR_STORED_ACCESS_TOKEN";
$code = $_REQUEST["code"];
// If we get a code, it means that we have re-authed the user
//and can get a valid access_token.
if (isset($code)) {
$token_url="https://graph.facebook.com/oauth/access_token?client_id="
. $app_id . "&redirect_uri=" . urlencode($my_url)
. "&client_secret=" . $app_secret
. "&code=" . $code . "&display=popup";
$response = file_get_contents($token_url);
$params = null;
parse_str($response, $params);
$access_token = $params['access_token'];
}
// Attempt to query the graph:
$graph_url = "https://graph.facebook.com/me?"
. "access_token=" . $access_token;
$response = curl_get_file_contents($graph_url);
$decoded_response = json_decode($response);
//Check for errors
if ($decoded_response->error) {
// check to see if this is an oAuth error:
if ($decoded_response->error->type== "OAuthException") {
// Retrieving a valid access token.
$dialog_url= "https://www.facebook.com/dialog/oauth?"
. "client_id=" . $app_id
. "&redirect_uri=" . urlencode($my_url);
echo("<script> top.location.href='" . $dialog_url
. "'</script>");
}
else {
echo "other error has happened";
}
}
else {
// success
echo("success" . $decoded_response->name);
echo($access_token);
}
// note this wrapper function exists in order to circumvent PHP’s
//strict obeying of HTTP error codes. In this case, Facebook
//returns error code 400 which PHP obeys and wipes out
//the response.
function curl_get_file_contents($URL) {
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_URL, $URL);
$contents = curl_exec($c);
$err = curl_getinfo($c,CURLINFO_HTTP_CODE);
curl_close($c);
if ($contents) return $contents;
else return FALSE;
}
?>
for more details info you can visit this link
Thanks
UPDATE
well you are doing something wrong you do not need to renew access token even if its expired all you need is sending user facebook id & content you want to post together to your server by ajax then post it without access token Just check up here
Publish Stream from the application - for non logged in user, using Graph API, php SDK
if you have publish_stream permission you dont need access token
this is documentation for publish_stream
https://developers.facebook.com/docs/reference/api/permissions/
Enables your app to post content, comments, and likes to a user's
stream and to the streams of the user's friends. With this permission,
you can publish content to a user's feed at any time, without
requiring offline_access. However, please note that Facebook
recommends a user-initiated sharing model.
I've got the problem in another project.
The way I handle it is to create a hidden iframe. The first time you need the user to accept the privilege, use your main window to redirect. then, when you are sure that the user has already accepted the privilege, use the hidden iframe to communicate with facebook.
The user will not see the "flash" because it will done in an iframe.
I've done it with GWT. Here is the code I used : it communicates with Facebook via the iframe and do a check on access token every 500ms to see if the token is valid.
The code is in java (compiled in javascript using gwt).
public class FacebookConnector extends Composite
{
public static final String ARG_ACCESS_TOKEN_EXPIRES = "fb_accessTokenExpires";
public static final String ARG_GAME_FACEBOOK_NAME = "gameFBName";
public static final String ARG_GAME_FACEBOOK_ID = "gameFBId";
private static FacebookConnectorUiBinder uiBinder = GWT.create(FacebookConnectorUiBinder.class);
interface FacebookConnectorUiBinder extends UiBinder<Widget, FacebookConnector>
{
}
private static FacebookConnector me;
public static FacebookConnector getInstance()
{
if (me == null)
{
me = new FacebookConnector();
}
return me;
}
#UiField
IFrameElement iframe;
private Date accessToken;
private Timer timer;
protected FacebookConnector()
{
initWidget(uiBinder.createAndBindUi(this));
if (ArgManager.getArg(ARG_ACCESS_TOKEN_EXPIRES) != null)
{
accessToken = new Date(Long.parseLong(ArgManager.getArg(ARG_ACCESS_TOKEN_EXPIRES)));
}
}
public void checkAccessToken(final AbstractAsyncCallback<Void> callback)
{
if (accessToken == null || accessToken.before(new Date()))
{
// send authentication
String url = "https://graph.facebook.com/oauth/authorize?client_id="
+ ArgManager.getArg(ARG_GAME_FACEBOOK_ID) + "&scope=user_birthday,email&redirect_uri="
+ ArgManager.getArg(ArgManager.ARG_URL_FACEBOOK_BASE) + "page/facebook-step2%3FgameName%3D"
+ ArgManager.getGameShortcut();
iframe.setSrc(url);
// check url
timer = new Timer() {
#Override
public void run()
{
ClientFactory.getInstance().getService().getAccessTokenExpires(new AbstractAsyncCallback<Date>() {
#Override
public void onSuccess(Date result)
{
super.onSuccess(result);
if (result != null && result.after(new Date()))
{
accessToken = result;
// call the callback
callback.onSuccess(null);
}
else
{
// try again in one second
timer.schedule(1000);
}
}
});
}
};
// launch timer in 500 milliseconds
timer.schedule(500);
}
else
{
callback.onSuccess(null);
}
}
}
Hope it will help you.
You can't simply do a server side exchange because that bypasses the user's control of the authorization.
Like others have said, you should use the javascript sdk to facilitate updating the access token. By default, it uses an iframe and falls back on a popup to handle communicating with Facebook. This should work well with your backbone.js application.
I like to define a javascript function that takes success and denied callbacks to execute after checking the facebook auth status:
function checkFBAuth(success, denied, scope) {
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
success(response);
} else {
FB.login(function(response) {
if (response.status === 'connected') {
success(response);
} else {
denied(response);
}
}, scope);
}
});
};
This will go ahead and run FB.login if the user's session has expired. In your success callback, you could also pass response.authResponse.signedRequest as signed_request in your AJAX POST data. This will allow most FB SDK's (for example, the PHP SDK) to recognize and validate the signed request and set the user id and access token. You could also pass the whole response.authResponse data with your POST. That has the accessToken, userID, and expiresIn time.
See https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/ for the docs on the Facebook Developers site.
Also, if you enable the offline access deprecation migration, you can exchange access token to extend the expiration date to 60 days instead of the default 2 hours. See https://developers.facebook.com/docs/offline-access-deprecation/
Like #ThinkingStiff said, the key point is that you need to call FB.getLoginStatus() on the client to get the latest access token
These days, all the cool kids are handling their logins and retrieving their access tokens via JavaScript with the SDK. And hey, why not? The users love it!
After the JavaScript SDK retrieves the access token, all AJAX requests to your server will have access to that access token as well. That is, it is automatically passed along with each
AJAX request in the form of a cookie.
So, on the server side, you can retrieve the access token via cookies (our friend StackOverflow has some Answers related to finding that cookie). However, if you do the other cool thing and use the PHP SDK you won't even have to give it a second thought, because it will automatically grab the cookie for you, if it's there!

Categories

Resources