Thinktecture IdentityServer3 Facebook Login Button Issue - facebook

I am using "IdentityServer3 - IdentityManager - MembershipReboot" in my project for User Management, Authentication & Resources Authorization.
I started from below sample and have gone good for creating users, authenticating them via /connect/token api and authorizing resources.
https://github.com/thinktecture/Thinktecture.IdentityServer.v3.Samples/tree/master/source/MembershipReboot
A brief architecture for my solution is
MySql as database. Communication via MembershipReboot.EF to MembershipReboot.
The client project is developed using html + angularjs.
Resources APIs are developed using Nancy & hosted on Owin+Katana in a seperate project.
Authentication Services(IdSvr+IdMgr+MR) are hosted in a seperate project.
Now I want to create a simple button/link clicking on which leads me to facebook login. The functionality of this button should be same as defined in IDSvr default login page's(https://localhost:44333/core/login?signin=4f909a877cc465afd26d72f60ec08f51) "Facebook button".
I have tried googled internet a lot but none of cases are matching my scenario.
I even tried to replicate the request-response behaviour of default IdSvr facebook login but that does not work as cookies are not being saved on end client.
Also i tried to hit "https://localhost:44333/core/signin-facebook" and getting response as HTTP/1.1 500 Internal Server Error from server. So i think might be I am somewhere wrong in setting facebook options in IdSrv project.
So if someone can just provide me a single IdSvr API to connect or tell me how to config Id Svr so that mapping a url can redirect it to facebook login. Or can tell me that where I am wrong in setting facebook authentication options in IdSrv.

A short and simple answer for my question is that I was looking for url.
https://localhost:44333/connect/authorize?client_id=implicitclient&response_type=token&scope=read&redirect_uri=http://localhost:8088/login/auth&nonce=random_nonce&acr_values=idp%3AFacebook&response_mode=form_post
Read further if you want to get better idea about this url
After lots of Hit&Trial & Study efforts, I have got solution for this. Well I think root cause for this problem was that sudden new technical things(Owin, Katana, OAuth, IdentityServer, IdentityManagement, MembershipReboot, Owin Facebook) and a meager time to understand them all.
I would advice folks that whoever is in same situation as me then first get an idea about OAuth. I found below link as a short and good one.
http://tutorials.jenkov.com/oauth2/index.html
After this I learnt that in our scenario we are dealing with two applications and hence two authentication.
For connecting User to Facebook. We created an app on developers.facebook.com
For connecting User to IdentityServer. We created a client in Clients.cs file on AuthenticationServices project.
So now here is the final solution.
localhost:44333 where AuthenticationService is running
locahost:8088 where FrontEnd services are running which iscalling AuthenticationService .
1. Create client app in AuthenticationServices as below
new Client
{
ClientName = "Implicit Clients",
Enabled = true,
ClientId = "implicitclient",
ClientSecrets = new List<ClientSecret>{
new ClientSecret("secret".Sha256())
},
Flow = Flows.Implicit,
RequireConsent = true,
AllowRememberConsent = true,
RedirectUris = new List<string>
{
"http://localhost:8088/login/auth" //This should be redirect url you want to hit after your app(not facebook app) redirects.
},
ScopeRestrictions = new List<string>
{
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
"read",
"write",
},
//SubjectType = SubjectTypes.Global,
AccessTokenType = AccessTokenType.Jwt,
IdentityTokenLifetime = 360,
AccessTokenLifetime = 360,
},
2 Create Authorize URL as below
var client = new OAuth2Client(new Uri("https://localhost:44333/core/connect/authorize"));
var startUrl = client.CreateAuthorizeUrl(
clientId: "implicitclient",
responseType: "token",
scope: "read",
redirectUri: "http://localhost:8088/login/auth",
nonce: "random_nonce",
responseMode: "form_post",
acrValues: "idp:Facebook");
The facebook app after successful authorization will redirect default to http://localhost:44333/signin-facebook. So no need to do any changes there.
Finally on http://localhost:8088/login/auth you will get access_token(+ few other parameters) after successful authentication. Here onwards you can use this token to access resources from Resources server.

Related

IdentityServer4 how to redirect the flow after login

I have installed an IdentityServer4 and a Client (Hybrid Mvc Client). All is ok. The following flow works:
1. User call secure page PageX (the controller is protected with Authorize attribute)
2. than system redirects the flow to Login page on IdentityServer
3. After authentication/authorization the IdentityServer redirect the user to url defined (redirect_uri) in the client configuration (page named Home) .
Now i don't know how to implement at the step 3 the redirection to PageX, the original page requested.
I have to create a custom AuthorizeAttribute to save on session storage the url of PageX and than using it in callback page? or is there any configuration on IdentityServer or client that could help me?
Thanks in advance
This is typically what you’d use the state parameter for. Your callback will receive the state value back unaltered and then you can verify the URL within is local and redirect to it automatically.
I’d recommend protecting the value from tampering using the DataProtection features in .net.
After successful login, by default the IdentityServer middleware tries to redirect to a consent page where to inform the user for the "allowed scopes". In this page are shown the claims that the client mvc site will receive access to: user identifier, user profile, email etc.
If you didn't setup such, you may set: "RequireConsent = false" when you define your MVC client. In such scenario the IdentityServer will redirect back to "RedirectUris" without showing consent page.
Example:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "mvc",
ClientName = "mvc Client",
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
},
RequireConsent = false
}
};
}
The other thing that I've noticed in the IdentityServer4 demos and quick starts is that you need the following NuGet packages:
For client website:
IdentityModel,
Microsoft.AspNetCore.All
For IdentityServer Authentication app:
IdentityServer4,
IdentityServer4.AccessTokenValidation,
IdentityServer4.AspNetIdentity,
Microsoft.AspNetCore.All
You may install these packages just to get the demo working.

Actions on Google implicit account linking works in simulator/browser, but not on device (via Google Home app)

I've implemented the implicit flow for Actions on Google account linking, and am using Dialogflow (previously API.AI) to define intents.
The full flow works in the device simulator (from AOG). The first intent gets a "It looks like your account isn't linked yet..." response, and the debug pane includes a URL to initiate linking:
https://assistant.google.com/services/auth/handoffs/auth/start?account_name=[account]#gmail.com&provider=[project_id]_dev&scopes=email&return_url=https://www.google.com/
If I follow this URI in a cache-less window:
I'm redirected to my app's authentication page
I choose to sign in with my Google account (same as [account] above)
I'm redirected to google.com with a success message in the URI bar
The simulator now accepts actions via my app and responds correctly
However, if I follow the same flow using a physical Google Home & the gH app for Android.
Device tells me account not yet linked
Open Google home and follow 'Link to [my app]' link
Browser opens to authentication page
Sign in as user
Redirected to a white page with a single link "Return to app", which has an href: about:invalid#zClosurez
Linking was unsuccessful, so additional attempts to run intents on the Google Home get the same "Account not yet linked" response.
I've inspected the intermediate access_token and state variables at length, and they all match and look to be correctly formatted:
Authentication URL (app sign in page): https://flowdash.co/auth/google?response_type=token&client_id=[client_id]&redirect_uri=https://oauth-redirect.googleusercontent.com/r/[project_id]&scope=email&state=[state]
After authenticating, redirected to (this is the white screen with 'return to app' broken link): https://oauth-redirect.googleusercontent.com/r/genzai-app#access_token=[token]&token_type=bearer&state=[state]
So, it seems there's something non-parallel about the way the simulator and physical devices work in terms of implicit flow account linking.
I've been struggling with this, and with the AOG support team for a very long time to no avail. Anyone else see a similar issue?
Updated with response redirect code:
Login handled by react-google-login component with profile & email scopes. On success we call:
finish_auth(id_token) {
let provider = {
uri: '/api/auth/google_auth',
params: ['client_id', 'redirect_uri', 'state', 'response_type'],
name: "Google Assistant"
}
if (provider) {
let data = {};
provider.params.forEach((p) => {
data[p] = this.props.location.query[p];
});
if (id_token) data.id_token = id_token;
api.post(provider.uri, data, (res) => {
if (res.redirect) window.location = res.redirect;
else if (res.error) toastr.error(res.error);
});
} else {
toastr.error("Provider not found");
}
}
provider.uri hits this API endpoint:
def google_auth(self):
client_id = self.request.get('client_id')
redirect_uri = self.request.get('redirect_uri')
state = self.request.get('state')
id_token = self.request.get('id_token')
redir_url = user = None
if client_id == DF_CLIENT_ID:
# Part of Google Home / API.AI auth flow
if redirect_uri == "https://oauth-redirect.googleusercontent.com/r/%s" % secrets.GOOGLE_PROJECT_ID:
if not user:
ok, _email, name = self.validate_google_id_token(id_token)
if ok:
user = User.GetByEmail(_email, create_if_missing=True, name=name)
if user:
access_token = user.aes_access_token(client_id=DF_CLIENT_ID)
redir_url = 'https://oauth-redirect.googleusercontent.com/r/%s#' % secrets.GOOGLE_PROJECT_ID
redir_url += urllib.urlencode({
'access_token': access_token,
'token_type': 'bearer',
'state': state
})
self.success = True
else:
self.message = "Malformed"
else:
self.message = "Malformed"
self.set_response({'redirect': redir_url}, debug=True)
I am able to make it work after a long time. We have to enable the webhook first and we can see how to enable the webhook in the dialog flow fulfillment docs If we are going to use Google Assistant, then we have to enable the Google Assistant Integration in the integrations first. Then follow the steps mentioned below for the Account Linking in actions on google:-
Go to google cloud console -> APIsand Services -> Credentials -> OAuth 2.0 client IDs -> Web client -> Note the client ID, client secret from there -> Download JSON - from json note down the project id, auth_uri, token_uri -> Authorised Redirect URIs -> White list our app's URL -> in this URL fixed part is https://oauth-redirect.googleusercontent.com/r/ and append the project id in the URL -> Save the changes
Actions on Google -> Account linking setup 1. Grant type = Authorisation code 2. Client info 1. Fill up client id,client secrtet, auth_uri, token_uri 2. Enter the auth uri as https://www.googleapis.com/auth and token_uri as https://www.googleapis.com/token 3. Save and run 4. It will show an error while running on the google assistant, but dont worry 5. Come back to the account linking section in the assistant settings and enter auth_uri as https://accounts.google.com/o/oauth2/auth and token_uri as https://accounts.google.com/o/oauth2/token 6. Put the scopes as https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email and weare good to go. 7. Save the changes.
In the hosting server(heroku)logs, we can see the access token value and through access token, we can get the details regarding the email address.
Append the access token to this link "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" and we can get the required details in the resulting json page.
`accessToken = req.get("originalRequest").get("data").get("user").get("accessToken")
r = requests.get(link)
print("Email Id= " + r.json()["email"])
print("Name= " + r.json()["name"])`
Not sure which python middleware or modules you are using but
self.set_response({'redirect': redir_url}, debug=True)
seems to be setting parameters for a returning a response which isn't correct. Instead you should redirect your response to the redirect_url. For example importing the redirect module in Flask or Django like:
from flask import redirect or from django.shortcuts import redirect
then redirect like:
return redirect(redirect_url)
It appears Google has made a change that has partially solved this problem in that it is now possible to complete the implicit account linking flow outside of the simulator, in the way outlined in my question.
It seems the problem stemmed from an odd handling (on the AOG side) of the client-side redirect case used after sign in with the Google sign-in button.
From Jeff Craig in this thread:
The current workaround, where we provide the "Return to app" link
currently what we're able to provide. The issue is with the way that
redirecting to custom-scheme URIs is handled in Chrome, specifically,
with regard to the redirect happening in the context of a user action.
XHR will break that context, so what is happening is that you click
the Google Sign-In Button, which triggers an XHR to Google's servers,
and then you (most likely) do a client-side redirect back to the
redirect_url we supply, our handler executes, and isn't able to do a
JS redirect to the custom scheme URI of the app, because were outside
of the context of a direct user click.
This is more of a problem with the Implicit (response_type=token) flow
than with the authorization code (response_type=code) flow, and the
"Return to app" link is the best fallback case we currently have,
though we are always looking for better solutions here as well.
The current behavior shows the 'Return to app' link, but as of last week, this link's href is no longer about:invalid#zClosurez, but instead successfully completes the sign-in and linking process. It's an odd and confusing UX that I hope Google will improve in the future, but it was sufficient to get my app approved by the AOG team without any changes to my flow.

REST web service Basic authentication + Client Facebook app authentication = double authentication?

I have built a web service based on a REST design. Of course some of the operations available on resources require authentication (delete a user for example). I use Basic authentication and so far everything is fine.
I have built a client to consume the web service : a set of Ajax function. Again, everything is fine (also for the Basic authentication).
I want now to create a whole web app that will use the set of Ajax function above to interact with the web service. But, to enhance the user experience, some of the web app functions will require Facebook authentication.
So here is my problem. The web app will require username and password to call the web service via the Basic authentication. But it will also require Facebook credentials to use Facebook API and the user will have to log in twice. Moreover, every time I will have to check if the Facebook user (currently logged in Facebook) corresponds to the user of the web service and it is quite troublesome.
Does anyone have an idea to simplify the process ?
It's a bit related to that post authentication-scheme-for-multi-tiered-web-application-utilizing-rest-api but I did not find any answer I could understand.
In such scenarios, I use only Facebook authentication. If user is logged into Facebook by JS SDK, you can simply get accessToken by FB.getAuthResponse().accessToken. Then, you can pass it into webservice and use it to authenticate on server side.
First, client side authentication with JS SDK:
/* assumed, that you alredy called FB.login() and stuff */
var accessToken = FB.getAuthResponse().accessToken;
$.ajax({
'url' : 'rest.php',
'type' : 'get',
'dataType' : 'json',
'data' : { accessToken : accessToken },
'success' : function(response) {
/* some fancy code, blah blah blah */
}
});
I use PHP SDK, so I'll show example in PHP.
<?php
require 'facebook.php';
$accessToken = $_GET['accessToken'];
$facebook = new Facebook(array(
'appId' => 'xxxxx',
'secret' => 'xxxxx'
));
$facebook->setAccessToken($accessToken);
if ($facebook->getUser()) {
// yaay, user is authenticated
echo json_encode($mySuperDuperSecretContentForLoggedUserOnly);
} else {
// authentication failed
echo json_encode(null);
}
If your main design uses basic authentication, but you want on some cases to be able to connect with facebook, then I suggest that you use an adapter/bridge on your server that will be able to take the facebook auth data and then handle the basic authentication itself.
That way the facebook users won't have to go through 2 authentication processes, and you don't break anything in your original flow.
It should not be hard to implement, when a user signs up just associate his fb account with a user in your system, then when he logs in with facebook take his id and log him into the system using the basic authentication.
One thing though, if you want to implement the facebook authentication using client side then you should send the auth data in https.

facebook-c#-sdk Windows Phone 7 Authentication - Why the secret?

This post { AppSecret with Windows Phone 7 }
indicates that the WP7 sample doesn't use the AppSecret to login from windows phone, but the current sample in the 5.3.2 download does use the secret.
However, FacebookOAuthClient.cs throws exceptions if it isn't provided. Also, http://blog.prabir.me/post/Facebook-CSharp-SDK-Writing-your-first-Facebook-Application.aspx
shows a sample without using the AppSecret.
Reading Facebooks developer docs it appears that the secret is intended for backend (webserver) auth to facebook, not client apps, and that it is poor practice, maybe insecure, and probably fattening to include your secret in your client application.
Do I misunderstand the guidance, or is there some way to authenticate with the facebook-c#-sdk without using the secret?
Thanks!
In WP 7.0 there was a problem with Fragment in Url (all after # was truncated). Facebook return auth token in Url Fragment, so without it it was impossible to authentificate like desktop/mobile app. The solution was to switch to Web mode, where you can receive auth token if you know AppSecret, so it was the only solution for that (but with security gaps).
In WP 7.1 Fragment Url bug was closed and now you can use normal authentification mode (without AppSecret on client).
If you could access anything of mine WITHOUT first having me authorize the app (solely using the app ID without an access token or a app secret), then that would be a HUGE security hole. Not only to my profile, but to any app out there since the app id is public.
The short answer is, you are required to have a user (or other type of) access token or an app secret to get information.
I figured out the problem was not with the SDK, but the Windows Phone 7 sample included. That sample uses the server-side flow. The changes necessary to the example were:
changing:
loginParameters["response_type"] = "code";
to:
loginParameters["response_type"] = "token";
and removing the entire labda function in webBrowser1_Navigated:
...
// The url is the result of OAuth 2.0 authentication.
if (oauthResult.IsSuccess)
{
var oauthClient = new FacebookOAuthClient { AppId = AppId, AppSecret = AppSecret };
// we got the code here
var code = oauthResult.Code;
oauthClient.ExchangeCodeForAccessTokenCompleted +=
(o, args) =>
{
...
and replaced it with this:
if (_fLoginMode && oauthResult.IsSuccess && !string.IsNullOrEmpty(oauthResult.AccessToken))
{
Dispatcher.BeginInvoke(() => NavigationService.Navigate(new Uri("/FacebookInfoPage.xaml?access_token=" + oauthResult.AccessToken, UriKind.Relative)));
}
And, of course removing the AppSecret constant

Can a html5 local app have an asp.net session? (local webapp for iPhone)

The context:
I'm actually developing a small web app (C#/MVC2). Users are going to use their iPhones (and probably Android phones in the future) to access it.
At the moment it's quite simple (it just shows some info and reports from our customer's ERP), and I decided to give a try at creating local webapp that the users could add to their iPhones, so that they had an icon for it and, most importantly, most files are locally cached, so that only the relevant data is obtained using json from the server.
The problem:
To authenticate users, a small form asks for username and password, and sends them to the server via ajax, which in turn validates the user and sets the authcookie. If the app is executed in Safari, everything works ok, but if it's executed locally (that is, in Mobile Safari directly from an icon), the server validates correctly the user, but this validation is lost when the next ajax call to recover data is made.
Does this mean that session cookies are not supported by Mobile Safari in webapps? I'm doing it wrong?
And most importantly: What's the best way to authenticate users in a local webapp that access remote data?
I'm not quite sure about what do you mean by local webapp. I assume that it's an HTTP web server running on localhost.
If that's the case, you need some protocol to communicate between http://localhost and http://yourwebsite.com, and that protocol should help localhost authenticate user via yourwebsite.com. I think OAuth might be what you're looking for.
The first time the user access your local webapp, he will be redirected to yourwebsite.com for the authentication. After that, yourwebsite.com will bring him back with an OAuth token. After verifying that token is valid from yourwebsite.com, localhost can serve user on its own.
(I realise I'm very late to this question, but anyway…)
Mobile Safari employs a slightly different web engine to that used in "home-screen apps" (i.e. web pages that you bookmark as self-contained icons on the iOS home screen).
Perhaps the issue you're seeing with cookies comes from that, rather than in Mobile Safari per se? I guess it's easy enough to test: if the app all works OK in Mobile Safari, and not from a home screen icon, there's your answer.
As an alternative take, rather than relying on authentication in the on-line version of the app, another approach that may work for you / your organisation is using the app in an unauthenticated state, but over a VPN for mobile workers? (This will still work OK as an offline web app).
Instead of using a cookie can't you have a ajax call to login that just returns the "authcookie"-value. The value can be saved using localStorage or similar.
http://dev.w3.org/html5/webstorage/
Later when you want to fetch something you can send this value to the server using a custom header (X-authentication or similar) or just append it as a GET-variable to the url.
Your best bet :
http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api
To access a protected resource, the client includes the access token
in the Authorization header of the HTTP request
Login :
var loginData = {
grant_type: 'password',
username: ...,
password: ...
};
$.ajax({
type: 'POST',
url: '/Token',
data: loginData
}).done(function (data) {
// Cache the access token in session storage.
sessionStorage.setItem(tokenKey, data.access_token);
});
Second request:
// If we already have a bearer token, set the Authorization header.
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: 'api/values/1',
headers: headers
}).done(function (data) {});
If you don't plan to use Web API, you must generate your own token and put it in every request data