I have a client application which authenticates with facebook and returns the token successfully. I would like to persist this data to the server without having to pass the entire object. Instead I would like to pass the resulting token from the client side authentication to my C# api in the authorization header and validate this token on the server.
Question, is it possible to verify the token on the server side? How to do it?
I am doing this via google, and need a facebook equivalent:
var googleResult = GoogleJsonWebSignature.ValidateAsync(
accessToken,
new GoogleJsonWebSignature.ValidationSettings
{
Audience = new[] { "secret key here.apps.googleusercontent.com" }
}
).GetAwaiter().GetResult();
Related
I have a .Net core app that runs as a background task. I want to be able to make calls to my web server on behalf of the users its processing data for (I guess this would be classed as user impersonation).
My web server uses IdentityServer4 and I can authenticate the app against it using the code in
https://identitymodel.readthedocs.io/en/latest/client/token.html
To summarize, define the client server side
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
Create a auth token client side and use in an a http call
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
...
This works, but when I user the bearer token I'm autheticated, but its not a 'user' thats logged in its the 'client' which has no claims.
So how do I get a bearer token that contains the claims I need to impersonate a user on the server?
You need to use the Authorization code flow instead of the clients credentials flow to get a token for the desired user. Then you save that token locally and use that to access the API on the behalf of the user.
If you have short-lived tokens, you can request a refresh token so that you can get new access tokens when it expires.
clients credentials flow is only used for machine to machine communication where no user is involved.
I have an app (currently in UWP) that makes use of MobileServiceClient and AutoRest to an Azure App Service API App. I successfully used the winfbsdk and can authenticate thru that and then get it to login to MobileService.LoginAsync with the FB access token as a JObject. I also take that JObject and send it in the x-zumo-auth header when making calls to the API App via AutoRest within the app.
What I would like to do is be able to authenticate using MicrosoftAccount. If I use MobileService.LoginAsync, I cannot get the proper token and pass it along to AutoRest - it always comes back as 401 Unauthorized.
I tried to used MSAL, but it returns a Bearer token and passing that along also comes back as 401 Unauthorized.
Is there any good way to do this? I started on the route of MSAL since that would support Windows desktop, UWP and Xamarin Forms which will be ideal. I just need info on how to get the proper token from it to pass along to an AutoRest HttpClient that goes back to the Azure App Service API App.
Update:
If I use the following flow, it works with Facebook, but not with MicrosoftAccount.
-Azure AppService with WebAPI (and swagger for testing via a browser)-Security setup through the Azure Dashboard on the service and configured to allow Facebook or MicrosoftAccount
1. On my UWP app, using winfbsdk, I login with Facebook, then grab the FBSession.AccessTokenData.AccessToken and insert that into a JObject:
JObject token = JObject.FromObject
(new{access_token = fbSession.AccessTokenData.AccessToken});
2. Login to MobileServiceClient
user = await App.MobileService.LoginAsync
(MobileServiceAuthenticationProvider.Facebook, token);
Login to API App with HttpClient and retrieve the token to use in X-ZUMO-AUTH
using (var client = new HttpClient())
{
client.BaseAddress = App.MobileService.MobileAppUri;
var jsonToPost = token;
var contentToPost = new StringContent(
JsonConvert.SerializeObject(jsonToPost),
Encoding.UTF8, "application/json");
var asyncResult = await client.PostAsync(
"/.auth/login/" + provider.ToString(),
contentToPost);
if (asyncResult.Content == null)
{
throw new InvalidOperationException("Result from call was null.");
return false;
}
else
{
if (asyncResult.StatusCode == System.Net.HttpStatusCode.OK)
{
var resultContentAsString = asyncResult.Content.AsString();
var converter = new ExpandoObjectConverter();
dynamic responseContentAsObject = JsonConvert.DeserializeObject<ExpandoObject>(
resultContentAsString, converter);
var applicationToken = responseContentAsObject.authenticationToken;
ApiAppClient.UpdateXZUMOAUTHToken(applicationToken);
}
}
}
ApiAppClient.UpdateXZUMOAUTH call just does the following:
if (this.HttpClient.DefaultRequestHeaders.Contains("x-zumo-auth") == true)
{
this.HttpClient.DefaultRequestHeaders.Remove("x-zumo-auth");
}
this.HttpClient.DefaultRequestHeaders.Add("x-zumo-auth", applicationToken);
Any subsequent calls using the ApiAppClient (created with AutoRest from the swagger json of my Azure AppService WebAPI) contain the x-zumo-auth header and are properly authenticated.
The problem occurs when trying to use MicrosoftAccount. I cannot seem to obtain the proper token to use in x-zumo-auth from either MSAL or LoginWithMicrosoftAsync.
For #1 above, when trying for MicrosoftAccount, I used MSAL as follows:
AuthenticationResult result = await MSAuthentication_AcquireToken();
JObject token = JObject.FromObject(new{access_token = result.Token});
And MSAuthentication_AcquireToken is defined below, using interfaces and classes as suggested in the Azure samples: https://github.com/Azure-Samples/active-directory-xamarin-native-v2
private async Task<AuthenticationResult> MSAuthentication_AcquireToken()
{
IMSAcquireToken at = new MSAcquireToken();
try
{
AuthenticationResult res;
res = await at.AcquireTokenAsync(App.MsalPublicClient, App.Scopes);
return res;
}
}
Update - ok with MobileServiceClient, but still not working with MSAL
I got it working with MobileServiceClient as follows:
1. Use MobileService.LoginAsync
2. Take the returned User.MobileServiceAuthenticationToken
3. Set the X-ZUMO-AUTH header to contain the User.MobileServiceAuthenticationToken
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
applicationToken = user.MobileServiceAuthenticationToken;
ApiAppClient.UpdateAppAuthenticationToken(applicationToken);
MSAL still not working!
So the original question still remains, what part of the token returned from MSAL do we need to pass on to X-ZUMO-AUTH or some other header so that calls to the Azure AppService WebAPI app will authenticate?
I have an app (currently in UWP) that makes use of MobileServiceClient and AutoRest to an Azure App Service API App. I successfully used the winfbsdk and can authenticate thru that and then get it to login to MobileService.LoginAsync with the FB access token as a JObject. I also take that JObject and send it in the x-zumo-auth header when making calls to the API App via AutoRest within the app.
According to your description, I assumed that you are using Client-managed authentication. You directly contact the identity provider and then provide the token during the login with your mobile back-end, then you could leverage MobileServiceClient.InvokeApiAsync to call your API APP, which would add the X-ZUMO-AUTH header with the value authenticationToken after you invoke MobileServiceClient.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
What I would like to do is be able to authenticate using MicrosoftAccount. If I use MobileService.LoginAsync, I cannot get the proper token and pass it along to AutoRest - it always comes back as 401 Unauthorized. I tried to used MSAL, but it returns a Bearer token and passing that along also comes back as 401 Unauthorized. Is there any good way to do this?
AFAIK, for the client-flow authentication patterns (AAD, Facebook, Google), the token parameter for LoginAsync would look like {"access_token":"{the_access_token}"}.
For the client-flow authentication (Microsoft Account), you could leverage MobileServiceClient.LoginWithMicrosoftAccountAsync("{Live-SDK-session-authentication-token}"), also you could use LoginAsync with the token parameter of the value {"access_token":"{the_access_token}"} or {"authenticationToken":"{Live-SDK-session-authentication-token}"}. I have tested LoginAsync with the access_token from MSA and retrieve the logged info as follows:
In summary, when you retrieve the authentionToken after you have logged with your mobile back-end, you could add the X-ZUMO-AUTH header to each of your API APP requests with the authentionToken.
For more details, you could refer to this official document about authentication works in App Service.
UPDATE
I have checked this https://github.com/Azure-Samples/active-directory-xamarin-native-v2 and used fiddler to capture the network packages when authenticating the user and get an access token. I found that MSAL is working against Microsoft Graph and REST and when the user is logged, you could only retrieve the access_token and id_token, and both of them could not be used for single sign-on with your mobile back-end.
While the official code sample about Client-managed authentication for Azure Mobile Apps with MSA is using the Live SDK. As the Live SDK REST API mentioned about signing users, you could get an access token and an authentication token which is used for single sign-on scenario. Also, I have checked the Server-managed authentication and found that app service authentication / authorization for MSA also uses the Live SDK REST API.
In summary, you could not use MSAL for client-managed authentication with MSA, for client-managed authentication, you need to leverage Live SDK to retrieve the authentication_token then invoke MobileServiceClient.LoginWithMicrosoftAccountAsync("{Live-SDK-session-authentication-token}") to retrieve the authenticationToken from your mobile backend. Or you could just leverage server-managed authentication for MSA. For more details about Live SDK, you could refer to LiveSDK.
I am using Ember CLI + ember simple auth torii link
to get authentication code from facebook for my ember app.
This is my environment.js file
ENV['torii'] = {
providers: {
'facebook-oauth2': {
apiKey: '865549306850377',
scope: 'email',
//redirectUri: window.document.location.href
redirectUri: 'http://localhost:4200/'
}
}
};
And my login controller looks like this -
facebook: function() {
var _this = this;
this.get('session').authenticate('simple-auth-authenticator:torii', 'facebook-oauth2').then(function(data){
console.log("status - ", _this.get('session'));
});
}
And login.hbs -
<li><button {{action "facebook" "facebook-oauth2"}}>Facebook OAuth2</button></li>
After the user clicks on the link, a facebook popup opens and ember app gets a token.
How do I get the user's email id along with this token ?
Has anybody faced a similar issue ?
So you're using oauth2, so all you're ever going to get is an authorization token. With this token, you can then go off and request other information. The token is basically just there to speed up the validation of users against your application.
If you want to get user information, you would need to create another method (probably on your server-side), which swaps the authorization code for an access token: like so (or alternatively you can request an access token directly, which would remove the need for a server-side solution.
Using the access Token you can then request the Facebook User ID, using the debug token endpoint, and after that you will be able get to any endpoint to get the information you need.
I found this question, which has an answer, but facebook changed the token format since then, now it is something like:
AAACEdEose0cBACgUMGMCRi9qVbqO3u7mdATQzg[more funny letters]ig8b3uss9WrhGZBYjr20rnJu263BAZDZD
In short, you cannot infer anything from it.
I also found the access token debugger, which shows the information I am looking for if you paste a token in, which is nice, but does not help me do it programmatically.
Point is, if someone gets a token for a user, he can use it to access the graph, which is what I do in my application - I want to be sure that people are forwarding the token that was issued to them by my application, and not another.
My application flow is:
Get access token from facebook (nothing special, in the way it is described in here , Server-side Flow. (also iPhone and android and used, but they have similar flows if I recall correctly))
[device] <-> [facebook]
With that access token, the device will access my application server with the token
[device] <-> [Jonathan's application]
At my server I attach the access token to the user and use that to give permissions to that user in my application. (using the facebook connect to authenticate users)
My application is secured, and the access done is also authenticated regardless of facebook, BUT! in this flow, the a weak link I identified is that I cannot authenticate for sure that the access token I got was signed for my application - I do not like it because I cache the tokens for offline use, I want to be 100% sure they are for my application, with my permissions.
So what will be the (best) way to authenticate that the token I got is related to my application (for relation to user, I use the token to access /me and see which user this token is for)
I do not need to decrypt the token (i guess its some sort of AES), I am just looking for an endpoint that will tell me the token matched my application id.
(EDIT: Using the C# SDK, if it matters.. But a graph/rest call to give that info is just as good as well :) )
https://graph.facebook.com/app/?access_token=[user_access_token]
This will return the app this token was generated for, you can compare that against your app's id.
The official graph endpoint for inspecting access tokens is:
GET graph.facebook.com/debug_token?
input_token=[user_access_token]&
access_token=[app_token_or_admin_token]
Example response:
{
"data": {
"app_id": 138483919580948,
"application": "Social Cafe",
"expires_at": 1352419328,
"is_valid": true,
"issued_at": 1347235328,
"metadata": {
"sso": "iphone-safari"
},
"scopes": [
"email",
"publish_actions"
],
"user_id": 1207059
}
}
app_token_or_admin_token can be obtained using the Graph API call:
GET graph.facebook.com/oauth/access_token?
client_id={app-id}
&client_secret={app-secret}
&grant_type=client_credentials
The debug_token endpoint will fail if that user_access_token doesn't belong to the app that generated the app_token_or_admin_token.
Relevant facebook documentation:
Inspecting access tokens:
https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/#checktoken
App Tokens:
https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
A documented way to ensure this is to use appsecret_proof.
GET graph.facebook.com/v2.5/me?access_token=[TOKEN]&appsecret_proof=[PROOF]
This verifies not only that it is a valid token, but also that the token belongs to the app. It also gets you user data in one go.
You can derive PROOF above in C# using this (from here):
public static string ComputeHmacSha256Hash(string valueToHash, string key)
{
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
byte[] valueBytes = Encoding.ASCII.GetBytes(valueToHash);
byte[] tokenBytes = new HMACSHA256(keyBytes).ComputeHash(valueBytes);
valueBytes = null;
keyBytes = null;
StringBuilder token = new StringBuilder();
foreach (byte b in tokenBytes)
{
token.AppendFormat("{0:x2}", b);
}
tokenBytes = null;
return token.ToString();
}
ComputeHmacSha256Hash(accessToken, appSecret);
Why not to use official way of doing things? Here's the request from FB's own video about security.
Request:
https://graph.facebook.com/debug_token?input_token={token-to-check}&access_token={app_id}|{app_secret}
Response:
"data": { "app_id": {token-app-id}, "user_id": {token-user-id}, ... }
Link to an official video: https://www.facebook.com/FacebookforDevelopers/videos/10152795636318553/
I made a screenshot so that time is visible, and you can find more info if you are interested.
I'm developing iPhone application, that is based on communication with server, and I want to use Facebook authentication mechanisms.
Basically, I think it should work like this:
In my iPhone app, user logs in to Facebook, using his email and password.
User allows access to his data for related Facebook application.
My iPhone app receives access token, after successful log in.
In further communication with my server, my iPhone application should use the received Facebook access token (for example: in queries).
When my server receives some query from iPhone app, with access token, it should ask Facebook that this token is valid (and for who), and if yes, server should assume that user is authenticated with Facebook.
My question is: how the server should ask Facebook if given access token is valid? I think I should somehow check if the token is valid for my Facebook app.
I've tried many Facebook queries to graph API, that I've found, but nothing worked as I expected. Can you provide me some example?
Here's a two step process you can use to validate that a user access token belongs to your App:
1) Generate an App Access token
(https://developers.facebook.com/docs/howtos/login/login-as-app/)
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID
&client_secret=YOUR_APP_SECRET
&grant_type=client_credentials
2) Debug the User Access token
(https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/)
https://graph.facebook.com/debug_token?
input_token=INPUT_TOKEN
&access_token=ACCESS_TOKEN
Where INPUT_TOKEN is the user access token you want to verify, and ACCESS_TOKEN is your app's token that you got from step 1.
The debug endpoint basically dumps all information about a token, so it'll respond with something like this:
{
data: {
app_id: YOUR_APP_ID,
is_valid: true,
metadata: {
sso: "iphone-safari"
},
application: YOUR_APP_NAMESPACE,
user_id: USER_ID,
issued_at: 1366236791,
expires_at: 1371420791,
scopes: [ ]
}
}
If that token isn't from "your app" then it will return an error response.
Update: this answer seems insecure since it doesn't validate the token
first as belonging to your app, see the comments, original answer as
follows:
I assume that you already have the access token in hand. In such a case the simplest way to validate an access token is to issue the following request
https://graph.facebook.com/me?fields=id&access_token=#accesstoken
Here replace #accesstoken with the access token you have. I will breakdown the url and will explain each.
We are issuing a graph api request here which will return the Facebook User Id of the owner of the access token as a JSON string. The keyword 'me' represents the currently logged in user or the owner of the access token. For this request access token is a mandatory parameter.
If the provided access token is not valid or expired Facebook will just return an error message of some sort.
For a valid access token the result will somehow look like this
{
"id": "ID_VALUE"
}
Another solution would be to use https://graph.facebook.com/app/?access_token=[user_access_token] as described by Get application id from user access token (or verify the source application for a token).
This appears to be an undocumented feature, but returns JSON containing the id of the app the token was generated for. If the token wasn't for your app, it returns a 400.
In the latest version of facebook (2.2) you can do it this way:
https://developers.facebook.com/docs/graph-api/reference/v2.2/debug_token
Sample output:
{
"data": {
"app_id": "THE APP ID",
"application": "APP NAME",
"expires_at": 1427245200,
"is_valid": true,
"scopes": [
"public_profile",
"basic_info",
"read_stream",
"email",
"publish_actions",
"read_friendlists",
"user_birthday",
"user_hometown",
"user_location",
"user_likes",
"user_photos",
"user_videos",
"user_friends",
"user_posts"
],
"user_id": "THE USER ID"
}
}
private function facebookRequestMe($access_token)
{
include_once "facebook.php";
$facebook = new Facebook(array(
"appId" => "your_application_id",
"secret" => "your_application_secret"
));
$facebook->setAccessToken($access_token);
return $facebook->api("/me", "GET");
}
You can download the Facebook SDK for PHP from GitHub.
If a user has passed you a Facebook UID that they claim is theirs and you want to check if it's legit, this is a Python function that will verify it against their access token (an implementation of Robin Jome's answer):
def verify_facebook_id(id, access_token):
import requests
import simplejson
params = {'fields': 'id', 'access_token': access_token}
text = requests.get("https://graph.facebook.com/me", params=params).text
json = simplejson.loads(text)
response_id = json["id"]
return response_id == id
This is the only secure method to verify user token using just one request:
https://graph.facebook.com/debug_token?input_token={token-to-inspect}&access_token={app_id}|{app_secret}
Note that a sign "|" in the above URL isn't used as OR but as separator and must be there after fill the other fields.
The response will be JSON looking like that:
{
data: {
app_id: {app_id},
application: {app_name},
expires_at: {some_number},
is_valid: {true|false}
scopes: {array_of_permissions},
user_id: {user_id}
}
}
Reference: https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
(above method is mentioned at the bottom of this section)
Along with an access token Facebook also sends an "expires_in" parameter, which is an offset value. Use that to compute for when the access token will expire as an NSDate. Then when you need to do a request compare the current date with the expiration date.
Also try to inspect the status codes and response strings Facebook sends back.