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.
Related
I'm using Google Sign-In for user authentication. Currently, anyone can log in but I'd like to manually add the users to the firebase authentication and only allow those users to sign in using Google Sign-in.
I can check if the currently signed-in user is new or existing by checking additionalUserInfo.isNewUser
const creds = await auth().signInWithCredential(googleCredential);
if (creds.additionalUserInfo?.isNewUser) {
await auth().signOut();
await auth().currentUser?.delete();
}
but by the time I get that information, onAuthStateChanged executes and redirects the user to the protected route.
It's counterintuitive to what we know as user flow but it is actually better to allow users to sign up and set conditions that will allow them to use your app. I wouldn't use isNewUser since it only fires once and an app refresh makes this defunct.
I suggest looking at custom claims, for example: allow users to access the page and content if the user has a 'isMember' set to true on their profile.
Custom claims can be read by the client and rules freely, and can only be edited through the admin-sdk. so an admin can setup a function to execute a command that updates the user, you could also do this with a node app with the admin-sdk installed.
you can read about Custom claims here.
client type: Spa
grant type: implicit or code(pkce)
As a user, I want to be able to get silently authenticated if I have already logged with my identity provider. If not stay on the client side just like a guest user. And if I want to login to the client I should be able to get authenticated manually through the login page.
This has both manual sign-in and automatic sign-in scenarios. How would you handle such cases in Open ID Connect?
By adding the prompt=none in client settings will silently get a new token if user has a valid session. But if not I want the user to be able to manually authenticate through the login page upon his/her wish.
If I set prompt=none this will never have any user interaction such as authentication.
tags: Silent authentication oidc, automatic login, SSO
It is quite a deep subject, and the flow typically works like this:
CLASSIC OIDC SOLUTION
User is redirected for each SPA
If signed in already at the IDP there is no login prompt
OAuth state is stored in local storage (though it is recommended to only store actual tokens in memory)
When an access token expires (or before) do an iframe token renewal with prompt=none
When a new browser tab is opened do an iframe token renewal to get tokens for that tab - to avoid a full redirect
When the user logs out remove OAuth state from local storage
The most widely used library is OIDC Client which will do a lot of the hard work for you. See also my blog post + code sample for how this looks visually.
PROBLEM AREAS
It is worth being aware also that iframe silent renewal does not work by default in the Safari browser in 2020. Some notes on this here.
Alternatively, you can use signinSilent(). I have used it on my login page ngOnInit (since AuthGuard will anyway redirect the user to login, I thought it will be the perfect place in my scenario).
// login.ts
ngOnInit(): void {
this.authService.signinSilent().then(_ => {}).catch(_ => {});
}
// authService
public signinSilent() {
return this.userManager.signinSilent();
}
signinSilent method will return the user object if user already has a valid session with idp. else it will throw an error, probably login_required.
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}" />
I ran into an probably, to some users, confusing issue:
I'm using in my application Firebase to store the users data etc. and to have the user's data protected I use the build in Google Authentication. I recognized that I can define scopes at this signIn process, which the user can/should allow, but I although noticed that you can't ask for ALL of the Google Scopes/Services, (all with Firebase built in functionality)
so I have to use the Google JS Library as well for authentication, to access the rest of the User data (I want to show the user in my Web-App) stored on Google.
And maybe it could be kind of confusing if the Google signin popup shows twice, one auth to Firebase and another auth to Google API's.
My question is, Is there a way to, maybe hand over the access_token at the Firebase signin process (or vice versa), that I don't have the user signin twice and the Google popup shows up only once?
You can call authWithOAuthToken with the token you get back from Google auth: See https://www.firebase.com/docs/web/api/firebase/authwithoauthtoken.html. From there:
This method accepts either a single string argument for OAuth credentials (such as an OAuth 2.0 bearer access token) or an object (such as a set of OAuth 1.0a credentials). Logging in with Facebook, GitHub, and Google with an OAuth token requires just a string access token:
// Authenticate with Facebook using an existing OAuth 2.0 access token
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.authWithOAuthToken("facebook", "<ACCESS-TOKEN>", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
The sample from the docs uses facebook, but the same applies to Google tokens.
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