loopback protected routes/ensure login - express

How do I ensure that a user is logged in before I render a view using loopback?
I can loggin in the front end using my angular app. But I wanted to block anonymous users from viewing the page.
I thought it would be a header, something like headers.authorization_token, but it does not seem to be there.
I am looking for something like connect-ensurelogin for passport, without having to use passport.

This is the $interceptor that solves your problem.
This code detects 401 responses (user not logged in or the access token is expired) from Loopback REST server and redirect the user to the login page:
// Inside app config block
$httpProvider.interceptors.push(function($q, $location) {
return {
responseError: function(rejection) {
if (rejection.status == 401) {
$location.nextAfterLogin = $location.path();
$location.path('/login');
}
return $q.reject(rejection);
}
};
});
And this code will redirect to the requested page once the user is logged in
// In the Login controller
User.login($scope.credentials, function() {
var next = $location.nextAfterLogin || '/';
$location.nextAfterLogin = null;
$location.path(next);
});

Here is one possible approach that has worked for me (details may vary):
Design each of the Pages in your Single Page Angular App to make at one of your REST API calls when the Angular Route is resolved.
Secure all of your REST API Routes using the AccessToken/User/Role/ACL scheme that LoopBack provides.
When no valid Access Token is detected on the REST Server side, pass back a 401 Unauthorized Error.
On the Client Side Data Access, when you detect a 401 on your REST Call, redirect to your Logic Route.
For the smoothest User Experience, whenever you redirect to Login, store the Route the User wanted to access globally
(localStore, $RootScope, etc.) and redirect back there when the User
Logs in and gets a valid Access Token.
Here is the LoopBack Access Control sample: https://github.com/strongloop/loopback-example-access-control

Related

Social Login authentication in an API

I am using Microsoft Owin/Katana and implemented the social login authentication in a Web Forms and MVC application which is working perfectly.
I have a requirement to convert the implementation into an API (controllers). In my MVC application, I am calling this line of code inside the login controller,
HttpContext.GetOwinContext().Authentication.Challenge(properties, AuthenticationProvider);
The user is redirected to the login screen of the provider (i.e. Google, Facebook, Twitter).
I was hoping to achieve the following,
User call /api/login controller of my API and it checks the provider and then using the access key and secret key, it returns the login URL as string or JSON and the consumer of the API will redirect to that URl. This way the secret and access keys are not exposed and just the URL is returned.
The problem is that there is no way I am able to find to get the login request URL of the provider. The Challenge method is void and it just redirects the user which in case of an API is not acceptable.
I have used the following code in a Web API,
var properties = new AuthenticationProperties() { RedirectUri = this.RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
MessageRequest.GetOwinContext().Authentication.Challenge(properties, AuthenticationProvider);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = MessageRequest;
return Task.FromResult(response);
When I call the API using Postman then it returns the whole HTML of the login page. When I call the API URL directly in the browser then it redirects to the login page correctly but I just want to return the Url of the provider and the consumer should redirect itself.
Is there any way to achieve this?

Ktor Keycloak Acess Token Route Protection

I'm using a server that authenticates through keycloak. In keycloak I have created a realm and am able to get an Access token as a response. This access token gets fed now into my Ktor application.
However, I'm not quite sure how to protect routes in an easy manner. I want to have some protected routes that have a authenticate("keycloakOAuth"){} scope around it which handles validating the access token and refreshing using the refresh token if the access token is expired.
Currently I have keycloak inside Ktor configured as this:
authenticate("keycloakOAuth") {
get("login") {}
route("/callback") {
// This handler will be executed after making a request to a provider's token URL.
handle {
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
if (principal != null) {
val response = principal as OAuthAccessTokenResponse.OAuth2
call.respondText { "Access token: ${response.accessToken}" }
} else {
call.respondText { "NO principal" }
}
}
}
}
This works fine because when I go to login I'm getting sent to the Keycloak login page and I can login. When I logged in the callback executes and I get my Access Token back.
When I'm trying to protect routes however, some odd stuff happens. I know that I need to validate the incoming JWT token. But I have no clue how to given the Ktor capabilities. The examples are also of little help, since they are quite vague.
Currently I have something like this:
authenticate("keycloakOAuth") {
get("/testAuth") {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
if(principal != null) {
call.respondText("Authenticated!")
} else {
call.respondText("Unauthenticated...")
}
}
}
But my application will always send me to the login page and then callback page, even though I am sending the Bearer token when I'm testing this call.
My question is:
How do I protect routes in a manner that they need a valid token, with the same syntax that Ktor uses (like authenticate(){}). Do I need to configure JWT for this?
When you request one of the routes under authenticate, the full cycle of OAuth authentication is triggered. This is because the Authentication plugin is designed so a client sends credentials and gets authenticated for each request. For some reason, OAuth integration was implemented on top of the Authentication plugin hence such unexpected behavior.
To solve your problem you can have only /login and /callback routes restricted. In the callback's handler save user ID and tokens in a session or in any other storage for future use. For other routes, you can check manually the fact that a user is authenticated and then use tokens from storage to acquire protected data from the resource server. For convenience, you can create some extension functions to minimize the amount of boilerplate code. Unfortunately, there is no built-in functionality to make it work out of the box.
You don't need to configure JWT for this.

How do I get react-native-inappbrowser-reborn to trigger success upon successful Facebook login

I'm trying to setup a manual flow for Facebook login, as per the docs at: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/
I've got my test Facebook app working as expected, i.e., I can login using a private web browser window fine. The URL I'm using is:
https://facebook.com/v3.3/dialog/oauth?client_id=<app_id>&display=popup&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html
Now within my React-Native app, I'm using react-native-inappbrowser-reborn to present a SFAuthenticationSession on iOS. As per their docs (at https://www.npmjs.com/package/react-native-inappbrowser-reborn), I'm doing the following:
const redirectUri = "https://www.facebook.com/connect/login_success.html"
const url = "https://facebook.com/v3.3/dialog/oauth?client_id="+appId+"&display=popup&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html"
InAppBrowser.isAvailable()
.then(() => {
InAppBrowser.openAuth(url, redirectUri, {
// iOS Properties
dismissButtonStyle: 'cancel',
// Android Properties
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: true,
})
.then((response) => {
// Only gets to this point if user explicitly cancels.
// So this does not trigger upon successful login.
})
// catch handlers follow
Using the above, my app correctly open up an in-app browser and I can login fine using a test user for my test app. Upon successful login though, I don't get redirected back to the .then completion handler. It just stays in the in-app browser view and I see the same message from Facebook that I see when logging in using a web browser. It says something like "Success. Please treat the url the same as you would a password", or something like that.
I may be missing something here, but I thought the purpose of passing redirectUri as an argument to openAuth was so that upon redirection to that URI, the completion handler would be triggered.
Question: How do I redirect back to the completion handler upon login success?
I think that you already have a solution but thought it might be useful for someone else facing this issue. If you don't have a solution so far follow my instructions:
You can't directly redirect back to your application using deep link, since Facebook will not call a link `like myapplicationname://mycustompath´. It's only possible to call links using the https-protocol (https://...).
The solution I'd suggest you to use is to redirect using your own API (Facebook -> Your API -> Deep Link Redirection). You will understand why this is required in the most of the real world applications at the end of the instructions.
Starting from your react-native app call the authorize endpoint of Facebook with a redirection to your application and set the global deeplink of your app as redirect uri.
InAppBrowser.close();
InAppBrowser.openAuth("https://graph.facebook.com/oauth/authorize?client_id=YOURCLIENTID&redirect_uri=https://YOURDOMAIN:PORT/auth/facebook", "{YOURAPPSDEEPLINKNAME}://{SOMEPATHYOUWANTTOEND}")
.then((response) => {
handleAuthorized(response, LOGINTYPE.FACEBOOK);
});
Now after login you'll be redirected to your API with the authorization code token as query parameter (e.g. https://YOURDOMAIN:PORT/auth/facebook?code=AVERYLONGCODESENTBYFACEBOOK)
Using this code token from the query parameter, you make another API Call to get the access_token for the user
{GET}: https://graph.facebook.com/v15.0/oauth/access_token?client_id=YOUR_CLIENT_ID&redirect_uri=https://YOURDOMAIN:PORT/auth/facebook&client_secret=YOUR_CLIENT_SECRET&code=AVERYLONGCODESENTBYFACEBOOK
Facebook's API will send you an answer as JSON with the access_token inside.
You can make another call using the access token of the user, to get the userId and the username
{GET}: https://graph.facebook.com/me?access_token=ACCESS_TOKEN_SENT_BY_FACEBOOK_IN_PREVIOUS_GET_REQUEST.
If you need the e-mail address for the user you have to make another call. Make sure you'd set the permission to read the e-mail address for your app on the developer portal of facebook.
The following request will return you the id, name and the email of the user
{GET}: https://graph.facebook.com/USERIDFROMPREVIOUSREQUEST?fields=id,name,email&access_token=ACCESSTOKEN
I think you want to save all these information to a database and create a session in order to keep the user logged in and therefore all the requests described will be useful for you in a real application.
After doing all the backend stuff, you're ready for the redirection using deep link. To do that, set a meta-tag to redirect the inappbrowser to your application:
<meta http-equiv="refresh" content="0; URL={YOURAPPSDEEPLINKNAME}://{SOMEPATHYOUWANTTOEND}" />

how to detect after FB authentication and before FB authorization

In my MVC application, I have below code in JQuery to check if user is connected to Facebook app or not
FB.login(function (response) {
switch (response.status) {
case "connected":
case "unknown":
break;
}
}, { scope: "#MyPermissions" });
Now, when I do FB login through my app, it authenticates and immediately starts FB app authorization and finally it comes to Connected case, when authorization is done.
My Question is : Can I detect when Facebook authentication in done and before authorization starts ? So that my debugger can catch the position before authorization takes place. I have to actually avoid the authorization.
Actually oAuth is two steps authorization you cannot stop it at authentication.
You can do a trick, Usually people are at already login to facebook therefore you can try getLoginStatus() on first load which will sure surely return you not_authorized as it has not yet authorize your app, You can perform your check their and then get user authorize.
FB.getLoginStatus(function(response) {
if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
EDIT: is this what you what? Facebook account delink or deauthorize facebook app and check status of linking from facebook app
Otherwise
Firstly Facebook login and app auth are inseparable for security reasons. Being logged into Facebook and being logged into Facebook through an app are different. To login using Facebook from an external site you are actually logging in through an app that requires the user to allow the app to access certain parts of their profile.
So when a user clicks login. First they will be asked to login to Facebook if they are not already. You can check this before login using FB.getLoginStatus https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
Once the user is logged into Facebook they will have to authenticate your app for you to gain access to their info. This info is also available using FB.getLoginStatus
What you need tough is an accessToken to make calls to the api with. The fb js sdk stores this internally when you run the login dialog. So if you don't login using it. The api calls will fail unless you build them yourself.
Based on the information give, I am assuming you want to avoid showing the logging / auth dialog every time a previously logged in user visits the page. This is the only situation I can think of that you might what to avoid showing the dialogs.
In this case you can use cookies and access tokens to keep a logged in state across page visit.
Use a cookie to store the accessToken locally after the first login. Then code your login logic to check for and validate the token on load or login.
This way returning to the site wont launch the login / auth dialog unless the accessToekn session runs out, but will just change the user state to logged in. Then using your access token build your graph api calls.
I use https://graph.facebook.com/oauth/access_token_info with parameter client_id: APPID, access_token: token to validate the token.
If the token is valid The the session is good, the user is logged in and has authorized the app. If this fails, the cookie is deleted and i kick of the login dialog.
There are a few more cases where you should delete the cookie, like authResponseChange or log out.
On that note; I believe what you want for post authorization is to subscribe to the authResponseChange event https://developers.facebook.com/docs/facebook-login/login-flow-for-web/. Here is a gutted implementation:
FB.Event.subscribe('auth.authResponseChange', function(response) {
if (response.authResponse) {
if (response.status === 'connected') {
// User logged in and User has authorized the app
}
else if (response.status === 'not_authorized') {
// User logged in but has not authorized app
}
else {
// User logged out
}
} else {
// No valid authResponse found, user logged out or should be logged out
}
});
There is more doco here https://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe/
And there are other events that you may be able to take advantage of
auth.login - fired when the auth status changes from unknown to connected
auth.authResponseChange - fired when the authResponse changes
auth.statusChange - fired when the status changes (see FB.getLoginStatus for additional information on what this means)
I haven't tried this for myself but a look through the FB.getLoginStatus page in the documentation suggests the following.
FB.getLoginStatus
FB.getLoginStatus allows you to determine if a user is logged in to
Facebook and has authenticated your app. There are three possible
states for a user:
the user is logged into Facebook and has authenticated your application (connected)
the user is logged into Facebook but has not authenticated your application (not_authorized)
the user is not logged into Facebook at this time and so we don't know if they've authenticated your application or not (unknown)
If I understand your question correctly, you may check the status for a case being not_authorized which will allow you to break out, in case the user is indeed logged in but has not authorized your application yet.
Make sure you place this case above the connected case though.
Also, this should work even though you're using FB.login instead of FB.getLoginStatus since according to the following quote from the same page,
The response object returned to all these events is the same as the
response from FB.getLoginStatus, FB.login or FB.logout. This response
object contains:
status The status of the User. One of connected, not_authorized or
unknown.
authResponse The authResponse object.
the returned object is the same.

Twitter #anywhere login implementation

I'm new to Twitter #anywhere. Is #anywhere login feature is intended to integrate our website with Twitter login? just like Facebook connect?
If yes, What is the callback url used for and what callback url should be provided?
I've tried to add this #anywhere login feature to my site and I'm testing it from a localhost / 127.0.0.1 site. I use the following code
twttr.anywhere(function (T) {
T("#twitter-login").connectButton({
authComplete: function(user) {
//triggered when auth completed successfully
alert('test login complete');
currentUser = T.currentUser;
var userInfo = document.getElementById('user-info');
userInfo.innerHTML =
'<img src="' + currentUser.data('profile_image_url') + '"/>'
+ currentUser.data('screen_name');
}
});
});
If I don't provide any callback url, after authorizing the user, Twitter give a
Sorry, something went wrong.
The provided callback url http://localhost:9000/ is not authorized for the client registered to 'http://127.0.0.1:9000'.
So then I provide a custom callback url with the following code
twttr.anywhere.config({ callbackURL: "http://127.0.0.1:9000"});
After adding that code, no error occurred but I ended up having two tabs opened of the same page. One is the page I use to login and the other one is the callback url page. And also the code inside the "authComplete:" section is not run, the alert is not shown.
Did I do anything wrong?
Thank you
Twitter #Anywhere users domains to authenticate the callback url.
For twitter server, 127.0.0.1 and localhost are two differente things. As your are using localhost as your callback url, try adding this domain on the authorized domains configuration on your app configuration att dev.twitter.com