Signing AWS Api Gateway request in Unity3D C# - unity3d

I'm using Unity3D v5.5.1, with AWS-SDK-Unity v3.3.37.0.
Since the Api Gateway doesn't generate an SDK for C#/Unity3D I'm trying to sign (SigV4) the request my self and have encountered difficulties.
I've tried both manually signing and using the AWS4Signer.cs class.
The Api Gateway method has the Invoke with caller credentials, and just returns a Hello World as a response.
Within unity I have a facebook login button which returns the FB credentials and tokens. Using Cognito Federated Identity's GetCredentialsAsync method I get an ImmutableCredentials object with the Key, Secret and a Token.
To access the api gateway url I'm using the AWS4Signer class here to construct a signed request. In the example below I've tried both adding the security token to the url parameters and without, also signing it and not signing with the token. All options don't work (As stated in this post)
This results in either the following responses:
1. The request signature we calculated does not match the signature you provided
The security token included in the request is invalid.
How can I correctly sign the request from Unity3D?
Thanks in advance
TestGet method:
IEnumerator TestGet (ImmutableCredentials response)
{
ApiGatewayConfig clientConfig = new ApiGatewayConfig(); // a class I created wrapping the ClientConfig.cs
var metrics = new RequestMetrics();
var awsAccessKeyId = response.AccessKey;
var awsSecretAccessKey = response.SecretKey;
var awsToken = response.Token;
AmazonWebServiceRequest req = new MyRequest(); // a clas I created wrapping the AmazonWebServiceRequest.cs class
var url = "https://<url_to_api>.execute-api.us-east-1.amazonaws.com/dev/securehello";
IRequest request = new DefaultRequest(req,"execute-api");
request.UseQueryString = true;
request.HttpMethod = "GET";
request.Endpoint = new System.Uri (url);
request.ResourcePath = url;
request.ContentStream = new MemoryStream();
request.Parameters.Add("X-Amz-Expires",AWS4PreSignedUrlSigner.MaxAWS4PreSignedUrlExpiry.ToString(CultureInfo.InvariantCulture));
request.AuthenticationRegion = "us-east-1";
request.AlternateEndpoint = RegionEndpoint.USEast1;
request.UseSigV4 = true;
request.Headers.Add("X-Amz-Security-Token",awsToken);
request.Parameters.Add("X-Amz-Security-Token",awsToken);
AWS4Signer signer = new AWS4Signer();
Debug.Log ("a");
signer.Sign(request,clientConfig,metrics,awsAccessKeyId,awsSecretAccessKey);
var signerRes = signer.SignRequest(request,clientConfig,metrics,awsAccessKeyId,awsSecretAccessKey);
Debug.Log ("b");
var myParams = string.Format("{0}&X-Amz-Security-Token={1}",signerRes.ForQueryParameters,awsToken);
var dict = myParams.Split('&').Select(p=> p.Split('=')).GroupBy(p => p[0]).Select(p => p.First()).ToDictionary(p => p[0], p=>System.Uri.UnescapeDataString(p[1]));
var myEncodedParams = string.Empty;
bool isFirst = true;
foreach (var key in dict.Keys) {
myEncodedParams += string.Format("{0}{1}={2}",isFirst ? "" : "&",key,WWW.EscapeURL(dict[key]));
isFirst = false;
}
var finalUrl = string.Format ("{0}?{1}", request.Endpoint.AbsoluteUri,myEncodedParams);
UnityWebRequest uwr = new UnityWebRequest (finalUrl, "GET", new DownloadHandlerBuffer (), null);
Debug.Log ( string.Format("\n\n\n{0}\n\n\n",finalUrl));
Debug.Log ("Starting WebRequest");
yield return uwr.Send();
if (uwr.isError) {
Debug.LogError (uwr.error);
} else {
Debug.Log (uwr.downloadHandler.text);
}
Helper classes:
public class ApiGatewayConfig : ClientConfig
{
private static readonly string UserAgentString =
InternalSDKUtils.BuildUserAgentString("3.3.37.0");
private string _userAgent = UserAgentString;
public ApiGatewayConfig ()
{
this.AuthenticationServiceName = "execute-api";
}
/// <summary>
/// The constant used to lookup in the region hash the endpoint.
/// </summary>
public override string RegionEndpointServiceName
{
get
{
return "execute-api";
}
}
/// <summary>
/// Gets the ServiceVersion property.
/// </summary>
public override string ServiceVersion
{
get
{
return "2015-07-09";
}
}
/// <summary>
/// Gets the value of UserAgent property.
/// </summary>
public override string UserAgent
{
get
{
return _userAgent;
}
}
}
public class MyRequest : AmazonWebServiceRequest
{
public MyRequest () {}
}

Solved.
I've created some examples showing how to do this. Still work in progress, example shows how to sign a POST request from Unity 3D to Api Gateway endpoint that has "Invoke with caller credentials" (AWS_IAM).
Unity 3D Client:
https://github.com/guywald/serverless-auth-msg-board-unity3d-client
AWS Serverless backend (using Serverless Framework):
https://github.com/guywald/serverless-auth-msg-board

Related

ASP.NET Core JWT and Claims

I have a question regarding JWT authentication in ASP.NET Core and Claims, because I don't know if I get everything correctly.
When I create a JWT token in ASP.NET I add some Claims, some of which can be custom. What happens when the request with JWT token is sent from the client to API. How is User.Claims filled ? Does it use the claims that are read from JWT?
I would like to create a custom Identity provider ( don't want to use this provided by ASP.NET), with my own tables for user data, roles etc. I don't want store all important data required to fulfill the policy in JWT token (the amount of information stored in token matters, as well as security matters). Is it possible to store only basic claims (like user id, name etc) in JWT token, and then re-fetch other required data DB/ Cache? Along with that, I would like to use the standard mechanism for [Authorize] and the Policy mechanism.
How to make this all work: Custom User Identity + JWT + Standard ASP.NET policy-based authorization + claims fetched from DB/Cache on every request? How to achieve this?
Asp Net Core
First step is write the method that configure Jwt authentication:
// Configure authentication with JWT (Json Web Token).
public void ConfigureJwtAuthService(IServiceCollection services)
{
// Enable the use of an [Authorize(AuthenticationSchemes =
// JwtBearerDefaults.AuthenticationScheme)]
// attribute on methods and classes to protect.
services.AddAuthentication().AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = JwtController.SecurityKey,
ValidAudience = JwtController.Audience,
ValidIssuer = JwtController.Issuer,
// When receiving a token, check that we've signed it.
ValidateIssuerSigningKey = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew when validating
// the lifetime. As we're creating the tokens locally and validating
// them on the same machines which should have synchronised time,
// this can be set to zero.
ClockSkew = TimeSpan.FromMinutes(0)
};
});
}
Now inside the ConfigureServices() method of the Startup.cs, we can call ConfigureJwtAuthService() method to configure the Jwt authentication.
This is the complete Startup.cs:
using System;
using Autofac;
using ExpertCodeBlogWebApp.Controllers;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace ExpertCodeBlogWebApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add
// services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Configure jwt autenticazione
ConfigureJwtAuthService(services);
// Repositories
services.AddScoped<IUserRepository, UserRepository>();
// Create the Autofac container builder for dependency injection
var builder = new ContainerBuilder();
// Add any Autofac modules or registrations.
builder.RegisterModule(new AutofacModule());
// Return ServiceProvider
var serviceProvider = services.BuildServiceProvider();
return serviceProvider;
}
// Configure authentication with JWT (Json Web Token).
public void ConfigureJwtAuthService(IServiceCollection services)
{
// Enable the use of an [Authorize(AuthenticationSchemes =
// JwtBearerDefaults.AuthenticationScheme)]
// attribute on methods and classes to protect.
services.AddAuthentication().AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = JwtController.SecurityKey,
ValidAudience = JwtController.Audience,
ValidIssuer = JwtController.Issuer,
// When receiving a token, check that we've signed it.
ValidateIssuerSigningKey = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew when validating
// the lifetime.
// As we're creating the tokens locally and validating them on the
// same machines which should have synchronised time, this can be
// set to zero.
ClockSkew = TimeSpan.FromMinutes(0)
};
});
}
// This method gets called by the runtime. Use this method to configure
// the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
// For dependency injection.
public class AutofacModule : Module
{
// Dependency Injection with Autofact
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UserRepository>().As<IUserRepository>()
.SingleInstance();
}
}
}
The JwtController.cs
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using ExpertCodeBlogWebApp.Domain.Models;
using ExpertCodeBlogWebApp.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
namespace ExpertCodeBlogWebApp.Controllers
{
[Route("api/[controller]")]
public class JwtController : Controller
{
#region Private Members
// JWT-related members
private TimeSpan TokenExpiration;
private SigningCredentials SigningCredentials;
// EF and Identity members, available through DI
private MyDbContext DbContext;
private IUserRepository _userRepository;
private readonly ILogger _logger;
#endregion Private Members
#region Static Members
private static readonly string PrivateKey = "my_PrivateKey";
public static readonly SymmetricSecurityKey SecurityKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(PrivateKey));
public static readonly string Issuer = "my_Issuer";
public static readonly string Audience = "my_Audience";
#endregion Static Members
#region Constructor
// I have used Autofac in the Startup.cs for dependency injection)
public JwtController(
MyDbContext dbContext,
IUserRepository userRepository,
ILogger<JwtController> logger)
{
_logger = logger;
_userRepository = userRepository;
// Instantiate JWT-related members
TokenExpiration = TimeSpan.FromMinutes(10);
SigningCredentials = new SigningCredentials(SecurityKey,
SecurityAlgorithms.HmacSha256);
// Instantiate through Dependency Injection with Autofact
DbContext = dbContext;
}
#endregion Constructor
#region Public Methods
// Manages the request for a new authentication or the refresh of an
// already established one
[HttpPost("token")]
public async Task<IActionResult>
Authentication([FromBody]JwtRequestViewModel jwt)
{
if (ModelState.IsValid)
{
string grantType = jwt.GrantType;
if (grantType == "password")
{
string userName = jwt.UserName;
string password = jwt.Password;
// Password check required
var user = await
_userRepository.GetUserInfoWithCheckPwd(userName, password);
// Check if user is expired (check the ExpireDate property)
if (UserExpired(user))
return BadRequest($"Account of {user.Name} expired!");
if (UserEnabled(user))
return await GenerateToken(user);
else
return BadRequest("User name or password invalid.");
}
}
else if (grantType == "refresh_token")
{
string userName = jwt.UserName;
// Refresh token (no password check required)
var user = await _userRepository.GetUserInfoByName(userName);
// Check if user is expired (check the ExpireDate property)
if (UserExpired(user))
return BadRequest($"Account of {user.Name} expired!");
string token = jwt.Token;
if (token == user.Token)
{
// Generate token and send it via a json-formatted string
return await GenerateToken(user);
}
else
{
return BadRequest("User token invalid.");
}
}
else
return BadRequest("Authentication type invalid.");
}
else
return BadRequest("Request invalid.");
}
#endregion Public Methods
#region Private Methods
private bool UserExpired(Users utente)
{
if (utente != null)
return utente.ExpireDate.CompareTo(DateTime.Now) < 0;
return true;
}
private bool UserEnabled(Users utente)
{
if (utente != null)
return utente.Enabled == true;
return false;
}
private JsonSerializerSettings DefaultJsonSettings
{
get
{
return new JsonSerializerSettings()
{
Formatting = Formatting.Indented
};
}
}
private async Task<IActionResult> GenerateToken(Users user)
{
try
{
if (user != null)
{
var handler = new JwtSecurityTokenHandler();
DateTime newTokenExpiration = DateTime.Now.Add(TokenExpiration);
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.Name, "TokenAuth"),
new[] { new Claim("ID", user.Id.ToString())}
);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = JwtController.Issuer,
Audience = JwtController.Audience,
SigningCredentials = SigningCredentials,
Subject = identity,
Expires = newTokenExpiration
});
string encodedToken = handler.WriteToken(securityToken);
// Update token data on database
await _userRepository.UpdateTokenData(user.Name, encodedToken,
newTokenExpiration);
// Build the json response
// (I use Automapper to maps an object into another object)
var jwtResponse = Mapper.Map<JwtResponseViewModel>(user);
jwtResponse.AccessToken = encodedToken;
jwtResponse.Expiration = (int)TokenExpiration.TotalSeconds;
return Ok(jwtResponse);
}
return NotFound();
}
catch(Exception e)
{
return BadRequest(e.Message);
}
}
#endregion
}
}
On my project I use Angular. For call JwtController method by Angular:
login(userName: string, password: string)
{
return this.getLoginEndpoint(userName, password)
.map((response: Response) => this.processLoginResponse(response));
}
getLoginEndpoint(userName: string, password: string): Observable<Response>
{
// Body
// JwtRequest is a model class that I use to send info to the controller
let jwt = new JwtRequest();
jwt.GrantType = "password";
jwt.UserName = userName;
jwt.Password = password;
jwt.ClientId = "my_Issuer";
// Post requiest (I use getAuthHeader that attach to the header the
// authentication token, but it can also be omitted because it is ignored
// by the JwtController
return this.http.post(this.loginUrl, JSON.stringify(jwt),
this.getAuthHeader(true))
}
protected getAuthHeader(includeJsonContentType?: boolean): RequestOptions
{
// Hera I use this.authService.accessToken that is a my service where
// I have store the token received from the server
let headers = new Headers({
'Authorization': 'Bearer ' + this.authService.accessToken });
if (includeJsonContentType)
headers.append("Content-Type", "application/json");
headers.append("Accept", `application/vnd.iman.v01+json,
application/json, text/plain, */*`);
headers.append("App-Version", "01");
return new RequestOptions({ headers: headers });
}
private processLoginResponse(response: Response)
{
// process the response..
}
On the controllers classes (or methods) that you want to be accessible only by authenticated users (not on your JwtController because its method must be accessible by all users) you can set:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
To call from Angular the controller method that require authentication, you need to attach the token into the header with the getAuthHeader() method.
I hope this post can help you.
yes it uses the claim stored in jwt token
look at the httpcontext object for claims that are stored in token when you created the token
this link can also be helpfull https://joonasw.net/view/adding-custom-claims-aspnet-core-2

Xamarin UWP HTTPS REST call returns gibberish

It seems I've hit a brick wall here and need new pairs of eyes to see the issue from different angles.
Context
I have a Xamarin.Forms PCL application for iOS, Android and UWP.
I need to call REST services; some are plain HTTP and other are HTTPS.
Issue
For iOS and Android everything work perfectly fine.
For UWP, HTTP calls work fine, HTTPS calls instead of the expected JSON, return gibberish. This is a sample:
"\u001f�\b\0\0\0\0\0\0��V\nK-�����W�244��Q\n-N-�K�MU�RO�+����I�ϪJ,(P�Q\n��\u0001I8��f�)�\u0002\02d+�>\0\0\0"
The code to make the call is in the PCL using the System.Net.Http framework library (see below).
Has anyone encountered a similar issue?
/// <summary>
/// A generic api call to a REST Web service, with data to be sent.
/// </summary>
/// <typeparam name="TParam">The type of object for the data that will be sent, used by post</typeparam>
/// <typeparam name="TResult">The type of object for the response of the server,expected to be of IClientApiModel type</typeparam>
///<param name="Uri">The service URI</param>
/// <param name="isPost">Whether the API should be treated as POST (<code>true</code>) or as a GET (<code>false</code>).</param>
/// <param name="param">The input data (can be null).</param>
/// <param name="authorization">The authorization header for the REST service</param>
/// <returns>An object of type TResult, and in case of Exception with the HasErrors property set to <code>true</code>.</returns>
protected async Task<TResult> RequestAsync<TParam, TResult>(string Uri, bool isPost, TParam param, AuthenticationHeaderValue authorization)
where TResult : IClientApiModel, new()
{
try
{
using (HttpClient client = new HttpClient())
{
using (HttpRequestMessage request = new HttpRequestMessage(isPost ? HttpMethod.Post : HttpMethod.Get, Uri))
{
request.Headers.Authorization = authorization;
if (param != null)
{
var data = new StringContent(JsonConvert.SerializeObject(param));
request.Content = data;
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
using (HttpResponseMessage response = await client.SendAsync(request)) //(request))
{
if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.NotFound)
{
return new TResult() { HasErrors = true, HasNoConnection = true };
}
return new TResult() { HasErrors = true };
}
var content = await response.Content.ReadAsStringAsync();
TResult model = content.IsJson() ?
JsonConvert.DeserializeObject<TResult>(content) :
new TResult() { HasErrors = true };
return model;
}
}
}
}
catch (WebException weberror)
{
throw weberror;
}
catch (Exception e)
{
var def = new TResult() { HasErrors = true };
return def;
}
}
Just to let everyone know... in the end it all came down to configure a handler for the HttpClient:
HttpClientHandler handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
_httpClient = new HttpClient(handler);

Sending a message to a azure service bus queue in .Net Core Core using the REST API

I want to send a message to my Azure Service Bus Queue in .Net Core but the WindowsAzure.ServiceBus Package is not compatible with .Net Core.
Can anyone show me how to send a message to the queue using the REST API?
While the current client is not .NET Core compatible, the new client, that is a work in progress, is 100% compatible. The pre-release package will be available on April 3rd and the status can be tracked here. You could pull down the course code and compile it already today with the caveat that API will be changing as the team is trying to flesh out the design details.
Can anyone show me how to send a message to the queue using the REST API?
As 4c74356b41 mentioned in his comment, we could send a message to Azure Service Bus queue via this REST API:
POST http{s}://{serviceNamespace}.servicebus.windows.net/{queuePath|topicPath}/messages
here is a example
In above request, I provide a Shared Access Signature (token), to generate a Shared Access Signature (token), please refer to this article.
Thanks to Fred's answer, I've expanded to include how to post the authentication header with signature.
public class AzureServiceBusSettings
{
public string BaseUrl { get; set; }
public string SharedAccessKey { get; set; }
public string SharedAccessKeyName { get; set; }
}
public interface IServiceBus
{
/// <summary>
/// Publish domain events to domain topic.
/// </summary>
Task PublishAsync<T>(T #event)
/// <summary>
/// Send commands to command queue.
/// </summary>
Task SendAsync<T>(T command)
}
public class ServiceBus : IServiceBus
{
private readonly AzureServiceBusSettings _settings;
public ServiceBus(IOptions<AzureServiceBusSettings> azureServiceBusSettings)
{
_settings = azureServiceBusSettings.Value;
}
/// <summary>
/// Publish domain events to domain topic.
/// </summary>
public async Task PublishAsync<T>(T #event)
{
await SendInternalAsync(#event, "domain");
}
/// <summary>
/// Send commands to command queue.
/// </summary>
public async Task SendAsync<T>(T command)
{
await SendInternalAsync(command, "commands");
}
private async Task SendInternalAsync<T>(T command, string queueName)
{
var json = JsonConvert.SerializeObject(command);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(_settings.BaseUrl);
try
{
var url = $"/{queueName}/messages";
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("SharedAccessSignature", GetSasToken(queueName));
var response = await httpClient.PostAsync(url, content);
// Success returns 201 Created.
if (!response.IsSuccessStatusCode)
{
// Handle this.
}
}
catch (Exception ex)
{
// Handle this.
// throw;
}
}
}
private string GetSasToken(string queueName)
{
var url = $"{_settings.BaseUrl}/{queueName}";
// Expiry minutes should be a setting.
var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
var signature = GetSignature(url, _settings.SharedAccessKey);
var token = $"sr={WebUtility.UrlEncode(url)}&sig={WebUtility.UrlEncode(signature)}&se={expiry}&skn={_settings.SharedAccessKeyName}";
return token;
}
private static string GetSignature(string url, string key)
{
var expiry = (int)DateTime.UtcNow.AddMinutes(20).Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
var value = WebUtility.UrlEncode(url) + "\n" + expiry;
var encoding = new UTF8Encoding();
var keyByte = encoding.GetBytes(key);
var valueBytes = encoding.GetBytes(value);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
var hashmessage = hmacsha256.ComputeHash(valueBytes);
var result = Convert.ToBase64String(hashmessage);
return result;
}
}
}
And a simple xunit test to post:
public class ServiceBusTests
{
public class FooCommand : ICommand
{
public Guid CommandId { get; set; }
}
private Mock<IOptions<AzureServiceBusSettings>> _mockAzureServiceBusOptions;
private ServiceBus _sut;
public ServiceBusTests()
{
var settings = new AzureServiceBusSettings
{
BaseUrl = "https://my-domain.servicebus.windows.net",
SharedAccessKey = "my-key-goes-here",
SharedAccessKeyName = "RootManageSharedAccessKey"
};
_mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusSettings>>();
_mockAzureServiceBusOptions.SetupGet(o => o.Value).Returns(settings);
_sut = new ServiceBus(
_mockAzureServiceBusOptions.Object);
}
[Fact]
public async Task should_send_message()
{
// Arrange.
var command = new FooCommand {CommandId = Guid.NewGuid()};
// Act.
await _sut.SendAsync(command);
// Assert.
// TODO: Get the command from the queue and assert something.
}
}

Error using facebook C# sdk with WPF web browser

I am new to facebook c# sdk. I followed the tutorial in this link.
I created an application that displays the user name after log in. Here is my code:
public partial class MainWindow : Window
{
private string appId = "appid";
private string extenededPermissions = "offline_access,publish_stream";
private Uri loginUrl = null;
private string accessToken = null;
private string userName = null;
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Function to get the login url
/// with the requested permissions
/// </summary>
private void GetLoginUrl()
{
dynamic parameters = new ExpandoObject();
// add the client id
parameters.client_id = appId;
// add the redirect uri
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
// requested response
parameters.response_type = "token";
// type of display
parameters.display = "popup";
// If extended permissions are present
if (!string.IsNullOrWhiteSpace(extenededPermissions))
parameters.scope = extenededPermissions;
// Create the login url
Facebook fc = new FacebookClient();
loginUrl = fc.GetLoginUrl(parameters);
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// get the login url
GetLoginUrl();
// Navigate to that page
webBrowser.Navigate(loginUrl);
}
private void webBrowser_Navigated(object sender, NavigationEventArgs e)
{
var fc = new FacebookClient();
FacebookOAuthResult fr;
// Check the returned url
if (fc.TryParseOAuthCallbackUrl(e.Uri, out fr))
{
// check if authentication is success or not
if (fr.IsSuccess)
{
getUserName(out userName);
}
else
{
var errorDes = fr.ErrorDescription;
var errorReason = fr.ErrorReason;
}
}
else
{
}
}
private void getUserName(out string name)
{
var fb = new FacebookClient(accessToken);
// Get the user details
dynamic result = fb.Get("me");
// Get the user name
name = result.name;
MessageBox.Show("Hai " + name + ",Welcome to my App");
}
}
My Problem is with the FacebookOAuthResult.
private void webBrowser_Navigated(object sender, NavigationEventArgs e)
{
var fc = new FacebookClient();
FacebookOAuthResult fr;
// Check the returned url
if (fc.TryParseOAuthCallbackUrl(e.Uri, out fr))
{
// check if authentication is success or not
if (fr.IsSuccess)
{
getUserName(out userName);
}
else
{
var errorDes = fr.ErrorDescription;
var errorReason = fr.ErrorReason;
}
}
else
{
}
}
After I logged in it is redirecting to redirect_uri. But the fc.TryParseOAuthCallbackUrl(e.Uri, out fr) fails though the webbrowser redirects to the Authentication successful page.
So I couldn't get the access token. What could the problem in my code be?
This doesn't answer the question, but I see you are asking for an offline_access permission. Facebook removed offline_access sometime ago. Instead you need an Extended Access Token. You get it by exchanging the access token you are trying to get, for an extended one. They last for about 2-3 months after which you have to get a new one.
Nevermind i have found out the solution..Thanks to the answers for the question!
I have added the Winforms web browser control to the wpf and the authentication is working.The problem is with WPF web browser. It simply omits the url after # token So the parseurl won't able to authenticate it.
Here's the modified code..
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// create the windows form host
System.Windows.Forms.Integration.WindowsFormsHost sample =
new System.Windows.Forms.Integration.WindowsFormsHost();
// create a new web browser
webBrowser = new System.Windows.Forms.WebBrowser();
// add it to winforms
sample.Child = webBrowser;
// add it to wpf
canvas1.Children.Add(sample);
webBrowser.Navigated += webBrowser_Navigated;
webBrowser.Navigate(loginURL);
}
void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
// do the authentication
var fc = new FacebookClient();
FacebookOAuthResult fr;
// Check the returned url
if (fc.TryParseOAuthCallbackUrl(e.Url, out fr))
{
// check if authentication is success or not
if (fr.IsSuccess)
{
accessToken = fr.AccessToken;
// Actions to do
}
else
{
var errordes = fr.ErrorDescription;
var errorreason = fr.ErrorReason;
}
}
else
{
//Not a valid url
}
}
The problem is solved!!

DotNetOpenAuth Claimed Identifier from Facebook is never the same

I'm using DotNetOpenAuth v3.5.0.10357 and each time a user authenticates against Facebook I get a different claimed identifier back. The token looks to be encrypted so I assume DNOA is somehow encrypting the token along with the expiry. Can anyone confirm this? Or am I using it wrong:
public ActionResult FacebookLogOn(string returnUrl)
{
IAuthorizationState authorization = m_FacebookClient.ProcessUserAuthorization();
if (authorization == null)
{
// Kick off authorization request
return new FacebookAuthenticationResult(m_FacebookClient, returnUrl);
}
else
{
// TODO: can we check response status codes to see if request was successful?
var baseTokenUrl = "https://graph.facebook.com/me?access_token=";
var requestUrl = String.Format("{0}{1}", baseTokenUrl, Uri.EscapeDataString(authorization.AccessToken));
var claimedIdentifier = String.Format("{0}{1}", baseTokenUrl, authorization.AccessToken.Split('|')[0]);
var request = WebRequest.Create(requestUrl);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var graph = FacebookGraph.Deserialize(responseStream);
var token = RelyingPartyLogic.User.ProcessUserLogin(graph, claimedIdentifier);
this.FormsAuth.SignIn(token.ClaimedIdentifier, false);
}
}
return RedirectAfterLogin(returnUrl);
}
}
Here's the code for FacebookAuthenticationResult:
public class FacebookAuthenticationResult : ActionResult
{
private FacebookClient m_Client;
private OutgoingWebResponse m_Response;
public FacebookAuthenticationResult(FacebookClient client, string returnUrl)
{
m_Client = client;
var authorizationState = new AuthorizationState(new String[] { "email" });
if (!String.IsNullOrEmpty(returnUrl))
{
var currentUri = HttpContext.Current.Request.Url;
var path = HttpUtility.UrlDecode(returnUrl);
authorizationState.Callback = new Uri(String.Format("{0}?returnUrl={1}", currentUri.AbsoluteUri, path));
}
m_Response = m_Client.PrepareRequestUserAuthorization(authorizationState);
}
public FacebookAuthenticationResult(FacebookClient client) : this(client, null) { }
public override void ExecuteResult(ControllerContext context)
{
m_Response.Send();
}
}
Also, I am using the RelyingPartyLogic project included in the DNOA samples, but I added an overload for ProcessUserLogin that's specific to facebook:
public static AuthenticationToken ProcessUserLogin(FacebookGraph claim, string claimedIdentifier)
{
string name = claim.Name;
string email = claim.Email;
if (String.IsNullOrEmpty(name))
name = String.Format("{0} {1}", claim.FirstName, claim.LastName).TrimEnd();
return ProcessUserLogin(claimedIdentifier, "http://facebook.com", email, name, claim.Verified);
}
It looks as though FacebookClient inherits from WebServerClient but I looked for the source on GitHub and I don't see a branch or a tag related (or at least not labeled) with the corresponding v3.5 version.
Facebook does not support OpenID. Claimed Identifier is an OpenID term. Facebook uses OAuth 2.0, so you're mixing up OpenID and OAuth.
Facebook sends a different access token every time, which is normal for the OAuth protocol. You have to use the access token to query Facebook for the user id that is consistent on every visit.
I think you need to add the offline_access permission in the token request as well, see https://developers.facebook.com/docs/reference/api/permissions/