OwinOAuthProviders RefreshToken flow - asp.net-web-api

I'm building a OwinOAuth (Oauth2) provider for my company to contribute here-https://github.com/TerribleDev/OwinOAuthProviders
The challenge I'm facing is that my company generates the refresh token too along with access token and provides the expiry time for both access token and refresh token. Access token expiry is set to 1hr and using Refresh token I can refresh access tokens again and again for 100 days.
Any implementations that I have seen create a RefreshTokenProvider which I don't need as my company already sends me that. The question I have here is this-
In the AuthenticationHandler class, since tokens are in memory and not saved anywhere and Oauth tokens are always generated by default, I'm not sure where the call/logic to get access tokens based on Refresh Tokens should be done.
Do I need to create a persistent store for all returned tokens and expiry and then match expiry to make a call to RefreshTokenHandler(grant_type=refresh_token) instead of AuthenticationHandler(grant_type=authorization_code) based on that?
I'm really confused here and any help with the implementation is appreciated.
EDIT-1
I have replaced my provider's name with Hidden everywhere.
Adding code for the HiddenAuthenticationHandler.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Owin.Security.Providers.Hidden.Provider;
using System.Net.Http.Headers;
using System.Net;
using System.Linq;
using Microsoft.Owin.Security.OAuth;
namespace Owin.Security.Providers.Hidden
{
public class HiddenAuthenticationHandler : AuthenticationHandler<HiddenAuthenticationOptions>
{
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string AuthorizationEndPoint = "https://appcenter.Hidden.com/connect/oauth2";
private const string TokenEndpoint = "https://oauth.platform.Hidden.com/oauth2/v1/tokens/bearer";
private const string UserInfoEndpoint = "https://sandbox-accounts.platform.Hidden.com/v1/openid_connect/userinfo";//"https://accounts.Hidden.com/v1/openid_connect/userinfo";//Nimisha
private const string RevokeEndpoint = "https://developer.api.Hidden.com/v2/oauth2/tokens/revoke";//Nimisha
private const string JWKSEndpoint = "https://oauth.platform.Hidden.com/op/v1/jwks";//Nimisha
private const string IssuerEndpoint = "https://oauth.platform.Hidden.com/op/v1";//Nimisha
private const string DiscoveryEndpoint = "https://developer.Hidden.com/.well-known/openid_configuration";//Nimisha
private static string refreshToken;
private static string refreshTokenExpiresIn;
private static string tokenType;
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public HiddenAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
//public async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
//{
// var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
// var currentClient = context.ClientId;
// if (originalClient != currentClient)
// {
// context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
// return Task.FromResult<object>(null);
// }
// // Change auth ticket for refresh token requests
// var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
// newIdentity.AddClaim(new Claim("newClaim", "newValue"));
// var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
// context.Validated(newTicket);
// return Task.FromResult<object>(null);
//}
public async Task<AuthenticationTicket> RefreshTokenAsync()
{
AuthenticationProperties properties = null;
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, _logger))
{
return new AuthenticationTicket(null, properties);
}
//var requestPrefix = Request.Scheme + "://" + Request.Host;
////var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
//var redirectUri = "https://be97d742.ngrok.io/signin-Hidden";
// Build up the body for the token request
var body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "refresh_token"),
new KeyValuePair<string, string>("refresh_token", refreshToken)
};
// Request the token
var refreshTokenResponse =
await _httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
refreshTokenResponse.EnsureSuccessStatusCode();
var text = await refreshTokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
var expires = (string)response.expires_in;
//string refreshToken = null;
refreshToken = (string)response.refresh_token;
refreshTokenExpiresIn = (string)response.x_refresh_token_expires_in;
tokenType = (string)response.token_type;
return new AuthenticationTicket(null, properties);
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
string code = null;
string state = null;
var query = Request.Query;
var values = query.GetValues("code");
if (values != null && values.Count == 1)
{
code = values[0];
}
values = query.GetValues("state");
if (values != null && values.Count == 1)
{
state = values[0];
}
properties = Options.StateDataFormat.Unprotect(state);
if (properties == null)
{
return null;
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, _logger))
{
return new AuthenticationTicket(null, properties);
}
var requestPrefix = Request.Scheme + "://" + Request.Host;
//var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
var redirectUri = "https://be97d742.ngrok.io/signin-Hidden";
// Build up the body for the token request
var body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("redirect_uri", redirectUri),
new KeyValuePair<string, string>("client_id", Options.ClientId),
new KeyValuePair<string, string>("client_secret", Options.ClientSecret)
};
// Request the token
var tokenResponse =
await _httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
var expires = (string)response.expires_in;
//string refreshToken = null; //Nimisha
refreshToken = (string)response.refresh_token;
refreshTokenExpiresIn = (string)response.x_refresh_token_expires_in;
tokenType = (string)response.token_type;
// Get the Hidden user
var userRequest = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.UserInfoEndpoint );
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var userResponse = await _httpClient.SendAsync(userRequest, Request.CallCancelled);
userResponse.EnsureSuccessStatusCode();
text = await userResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
var context = new HiddenAuthenticatedContext(Context, user, accessToken, expires, refreshToken, refreshTokenExpiresIn, tokenType)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Sub))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Sub, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.GivenName))
{
string Name = "";
Name = context.GivenName;
if (!string.IsNullOrEmpty(context.FamilyName))
{
Name = Name +" " + context.FamilyName;
}
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, Name, XmlSchemaString, Options.AuthenticationType));
context.Identity.AddClaim(new Claim("urn:Hidden:name", Name, XmlSchemaString, Options.AuthenticationType));
}
if ((!string.IsNullOrEmpty(context.Email)) && (!string.IsNullOrEmpty(context.EmailVerified)))
{
if (String.Equals(context.EmailVerified, "true"))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, Options.AuthenticationType));
}
}
if ((!string.IsNullOrEmpty(context.PhoneNumber)) && (!string.IsNullOrEmpty(context.PhoneNumberVerified)))
{
if (String.Equals(context.PhoneNumberVerified, "true"))
{
context.Identity.AddClaim(new Claim(ClaimTypes.MobilePhone, context.Email, XmlSchemaString, Options.AuthenticationType));
}
}
if (!string.IsNullOrEmpty(context.StreetAddress))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.StreetAddress, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Locality))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Locality, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Region))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Locality, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.PostalCode))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Locality, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Country))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Locality, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = properties;
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception ex)
{
_logger.WriteError(ex.Message);
}
return new AuthenticationTicket(null, properties);
}
protected override Task ApplyResponseChallengeAsync()//Nimisha-1
{
if (Response.StatusCode != 401)
{
return Task.FromResult<object>(null);
}
//If Response is 401 then check here based on saved Expiration duration for token and refreshtoken and then make either Refresh Token call or Access Token call
//if(SavedTokenExpiryTime is not null and is >=DateTime.Now) then call RefreshToken api call
//or
//Call below
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null) return Task.FromResult<object>(null);
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
Request.PathBase;
var currentUri =
baseUri +
Request.Path +
Request.QueryString;
//var redirectUri =
// baseUri +
// Options.CallbackPath;//Nimisha check callback path
var redirectUri = "https://be97d742.ngrok.io/signin-Hidden";
var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = currentUri;
}
// OAuth2 10.12 CSRF
GenerateCorrelationId(properties);
// comma separated
var scope = string.Join(" ", Options.Scope);
var state = Options.StateDataFormat.Protect(properties);
//Nimisha //"https://appcenter.Hidden.com/connect/oauth2" + //Nimisha check
var authorizationEndpoint =
Options.Endpoints.AuthorizationEndpoint+
"?client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&response_type=code" +
"&scope=" + Uri.EscapeDataString(scope) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
return Task.FromResult<object>(null);
}
public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
{
if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path) return false;
// TODO: error responses
var ticket = await AuthenticateAsync();
if (ticket == null)
{
_logger.WriteWarning("Invalid return state, unable to redirect.");
Response.StatusCode = 500;
return true;
}
var context = new HiddenReturnEndpointContext(Context, ticket)
{
SignInAsAuthenticationType = Options.SignInAsAuthenticationType,
RedirectUri = ticket.Properties.RedirectUri
};
await Options.Provider.ReturnEndpoint(context);
if (context.SignInAsAuthenticationType != null &&
context.Identity != null)
{
var grantIdentity = context.Identity;
if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
{
grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType);
}
Context.Authentication.SignIn(context.Properties, grantIdentity);
}
if (context.IsRequestCompleted || context.RedirectUri == null) return context.IsRequestCompleted;
var redirectUri = context.RedirectUri;
if (context.Identity == null)
{
// add a redirect hint that sign-in failed in some way
redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied");
}
Response.Redirect(redirectUri);
context.RequestCompleted();
return context.IsRequestCompleted;
}
}
}

Related

Identitty4 Access Token from Android and token lenght

I have a console application where I use to test some code. In my console app everything works as expected. Same code implemented in the xamarin forms and I get an un tI sent a request to identity4 to get a token and it works receiving a token 685 characters of length. The same code that I use in the console is implemented in android on a button click action and I receive the token as well. I try to use the access token to my protected api and I get Unauthorized. I try to use the token in postman and I get Unauthorized. If I use the token received in my console in the postman it works! What I realize is that in the console app the access token have a length of 865 characters and in android the access token have a length of 864. I checked the length from the content which I receive before apply NewtonSoft Json converter
My console code:
private static async Task Main()
{
Console.WriteLine("Request For a token");
//var handler = new HttpClientHandler();
//handler.ClientCertificateOptions = ClientCertificateOption.Manual;
//handler.SslProtocols = SslProtocols.Tls12;
//handler.ClientCertificates.Add(new X509Certificate2("rsaCert.pfx","1234"));
//var client = new HttpClient(handler);
// discover endpoints from metadata
var client = new HttpClient();
//client.BaseAddress = new Uri("http://localhost:5000");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("scope", "catalogapi1"),
new KeyValuePair<string, string>("client_id", "ro.client"),
new KeyValuePair<string, string>("client_secret", "secret")
});
var result = await client.PostAsync("http://localhost:5000/connect/token", content);
string resultContent = await result.Content.ReadAsStringAsync();
Console.WriteLine(resultContent);
ServerReponse responseToken = JsonConvert.DeserializeObject<ServerReponse>(resultContent);
Console.WriteLine("Token Lenght: "+ responseToken.access_token.Length);
Console.WriteLine("\n\n");
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(responseToken.access_token);
var response = await apiClient.GetAsync("http://localhost:5100/api/v1/products");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content2 = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content2));
}
Console.WriteLine("Press a key to close the application");
Console.ReadLine();
}
And in my Android event click action:
async void OnButtonClicked(object sender, EventArgs args)
{
var client = new HttpClient();
//client.BaseAddress = new Uri("http://localhost:5000");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("scope", "test"),
new KeyValuePair<string, string>("client_id", "ro.client"),
new KeyValuePair<string, string>("client_secret", "secret")
});
var result = await client.PostAsync("http://10.0.2.2:5000/connect/token", content);
string resultContent = await result.Content.ReadAsStringAsync();
Console.WriteLine(resultContent);
ServerReponse responseToken = JsonConvert.DeserializeObject<ServerReponse>(resultContent);
// call api
var apiClient = new HttpClient();
apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", responseToken.access_token.Trim());
// apiClient.SetBearerToken(responseToken.access_token);
var response = await apiClient.GetAsync("http://10.0.2.2:5100/api/v1/products");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content2 = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content2));
}
Console.WriteLine("Press a key to close the application");
Console.ReadLine();
}
public class ServerReponse
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string token_type { get; set; }
public string scope { get; set; }
}

Set HTTPOnly cookie from the ASP.net Web Api

I am trying to implement jwt token authentication in my web api. I have a front end app which is in angular 8. i want to save the token in cookie.how can i achieve this?? what will be the syntax for the subsequent call from the angular,if i save the token in cookie after login.
TokenValidationHandler
internal class TokenValidationHandler : DelegatingHandler
{
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
return false;
}
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
//determine whether a jwt exists or not
if (!TryRetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = "http://localhost:50191",
ValidIssuer = "http://localhost:50191",
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = securityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode){ });
}
public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (expires != null)
{
if (DateTime.UtcNow < expires) return true;
}
return false;
}
}
LoginController(will generate the token)
public class LoginController : ApiController
{
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="pass" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
//return the token
return Ok<string>(token);
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
private string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddSeconds(30);
//http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
var tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey,Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);
//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer:"http://localhost:50191",audience:"http://localhost:50191",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
}
Firstly, the httponly cookie cannot be accessed from JavaScript.
Secondly, please refer to the code below to return the cookie along with the response.
private HttpResponseMessage setTokenCookie(LoginResponse response)
{
var responseMessage = Request.CreateResponse(HttpStatusCode.OK, response);
var cookie = new CookieHeaderValue("token", response.Token)
{
Expires = DateTimeOffset.Now.AddDays(7),
HttpOnly = true
};
responseMessage.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return responseMessage;
}
You can use the function mentioned above in your controller action method as shown below to return the cookie.
var responseWithCookie = setTokenCookie(response);
return ResponseMessage(responseWithCookie);
I've assumed that LoginResponse class has Token property.

How to validate JWT Token using JWKS in Dot Net Core

In C#, I need to validate the Bearer Token against the JWKS (Json object which represent the set of keys like below)
{
"keys":[
{
"e":"AQAB",
"kid":"unique key",
"kty":"RSA",
"n":"some value"
}
]
}
You can do this using Microsoft's Nuget packages Microsoft.IdentityModel.Tokens and System.IdentityModel.Tokens.Jwt
Use following code to create token validator:
private static bool ValidateToken(string token, TokenValidationParameters validationParameters)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
return validatedToken != null;
}
catch (Exception)
{
return false;
}
}
And for usage you have to load JWKS and select a key for validation parameters:
var jwksJson = #"
{
""keys"":[
{
""e"":""AQAB"",
""kid"":""unique key"",
""kty"":""RSA"",
""n"":""some value""
}
]
}";
var token = "eyJhb...";
var jwks = new JsonWebKeySet(jwksJson);
var jwk = jwks.Keys.First();
var validationParameters = new TokenValidationParameters
{
IssuerSigningKey = jwk,
ValidAudience = "", // Your API Audience, can be disabled via ValidateAudience = false
ValidIssuer = "" // Your token issuer, can be disabled via ValidateIssuer = false
};
var isValid = ValidateToken(token, validationParameters);

Response for preflight is invalid (redirect) using Asp.Net Web API

I'm developing a website which uses AngularJS in the frontend and ASP.NET web API in the backend. I'm trying to configure external logins(Facebook) for my site. I have already enabled Cors on the API. The order of API calls are:
API call to
api/Account/ExternalLogins?returnUrl=%2F&generateState=true
to get a list of external login providers.
This returns
[{"name":"Facebook",
"url":"/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx",
"state":"xxxxxxxxxx"}]
Now I send a GET request to url returned earlier. This triggers a preflight request to https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx which gives error. This is never redirected to Facebook Login Page.
Both the request and response headers for /api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx contains Access-Control-Allow-Origin:*
But for the preflight request https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx Access-Control-Allow-Origin header is missing.
The code snippets are mentioned below.
app.js
$httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
$httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Content-Length, X-Requested-With';
$httpProvider.defaults.headers.common['Access-Control-Allow-Credentials'] = true;
$httpProvider.defaults.headers.common['Access-Control-Allow-Method'] = 'GET, PUT, POST, DELETE, OPTIONS';
Start.Auth.cs
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCors(CorsOptions.AllowAll);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/api/Account/ExternalLogin"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
//Provider = new AuthorizationServerProvider()
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
AppId = "xxxxxxxxx",
AppSecret = "xxxxxxxxx",
BackchannelHttpHandler = new FacebookBackChannelHandler(),
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(0);
}
},
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
SendAppSecretProof = true
};
facebookOptions.Scope.Add(ConfigurationManager.AppSettings["Facebook_Scope"]);
facebookOptions.AuthenticationMode = AuthenticationMode.Passive;
app.UseFacebookAuthentication(facebookOptions);
AccountController
[Authorize]
[EnableCors(origins: "*", headers: "*", methods: "*")]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private const string LocalLoginProvider = "Local";
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UserInfo")]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
return new UserInfoViewModel
{
Email = User.Identity.GetUserName(),
HasRegistered = externalLogin == null,
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
};
}
// POST api/Account/Logout
[Route("Logout")]
[AllowAnonymous]
public IHttpActionResult Logout()
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return Ok();
}
// POST api/Account/AddExternalLogin
[Route("AddExternalLogin")]
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
&& ticket.Properties.ExpiresUtc.HasValue
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
{
return BadRequest("External login failure.");
}
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
if (externalData == null)
{
return BadRequest("The external login is already associated with an account.");
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// GET api/Account/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
{
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
externalLogin.ProviderKey));
bool hasRegistered = user != null;
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
else
{
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
}
// GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
[AllowAnonymous]
[Route("ExternalLogins")]
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationDescription description in descriptions)
{
ExternalLoginViewModel login = new ExternalLoginViewModel
{
Name = description.Caption,
Url = Url.Route("ExternalLogin", new
{
provider = description.AuthenticationType,
response_type = "token",
client_id = Startup.PublicClientId,
redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
state = state
}),
State = state
};
logins.Add(login);
}
return logins;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null)
{
return InternalServerError();
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
}
I know this is achievable using MVC Account Controller. I'm looking for a way to do this entirely using web api with no dependency on MVC.
Thanks!

How to send pre request before every request in windows phone 7

I want to send one pre request to my server before send every request. From that pre request I will receive the token from my server and than I have to add that token into all the request. This is the process.
I have try with some methods to achieve this. But I am facing one problem. That is, When I try to send pre request, it is processing with the current request. That mean both request going parallel.
I want to send pre request first and parse the response. After parsing the pre request response only I want to send that another request. But first request not waiting for the pre request response. Please let me any way to send pre request before all the request.
This is my code:
ViewModel:
`public class ListExampleViewModel
{
SecurityToken sToken = null;
public ListExampleViewModel()
{
GlobalConstants.isGetToken = true;
var listResults = REQ_RESP.postAndGetResponse((new ListService().GetList("xx","xxx")));
listResults.Subscribe(x =>
{
Console.WriteLine("\n\n..................................2");
Console.WriteLine("Received Response==>" + x);
});
}
}`
Constant Class for Request and Response:
`public class REQ_RESP
{
private static string receivedAction = "";
private static string receivedPostDate = "";
public static IObservable<string> postAndGetResponse(String postData)
{
if (GlobalConstants.isGetToken)
{
//Pre Request for every reusest
receivedPostDate = postData;
GlobalConstants.isGetToken = false;
getServerTokenMethod();
postData = receivedPostDate;
}
HttpWebRequest serviceRequest =
(HttpWebRequest)WebRequest.Create(new Uri(Constants.SERVICE_URI));
var fetchRequestStream =
Observable.FromAsyncPattern<Stream>(serviceRequest.BeginGetRequestStream,
serviceRequest.EndGetRequestStream);
var fetchResponse =
Observable.FromAsyncPattern<WebResponse>(serviceRequest.BeginGetResponse,
serviceRequest.EndGetResponse);
Func<Stream, IObservable<HttpWebResponse>> postDataAndFetchResponse = st =>
{
using (var writer = new StreamWriter(st) as StreamWriter)
{
writer.Write(postData);
writer.Close();
}
return fetchResponse().Select(rp => (HttpWebResponse)rp);
};
Func<HttpWebResponse, IObservable<string>> fetchResult = rp =>
{
if (rp.StatusCode == HttpStatusCode.OK)
{
using (var reader = new StreamReader(rp.GetResponseStream()))
{
string result = reader.ReadToEnd();
reader.Close();
rp.GetResponseStream().Close();
XDocument xdoc = XDocument.Parse(result);
Console.WriteLine(xdoc);
return Observable.Return<string>(result);
}
}
else
{
var msg = "HttpStatusCode == " + rp.StatusCode.ToString();
var ex = new System.Net.WebException(msg,
WebExceptionStatus.ReceiveFailure);
return Observable.Throw<string>(ex);
}
};
return
from st in fetchRequestStream()
from rp in postDataAndFetchResponse(st)
from str in fetchResult(rp)
select str;
}
public static void getServerTokenMethod()
{
SecurityToken token = new SecurityToken();
var getTokenResults = REQ_RESP.postAndGetResponse((new ServerToken().GetServerToken()));
getTokenResults.Subscribe(x =>
{
ServerToken serverToken = new ServerToken();
ServiceModel sm = new ServiceModel();
//Parsing Response
serverToken = extract(x, sm);
if (!(string.IsNullOrEmpty(sm.NetErrorCode)))
{
MessageBox.Show("Show Error Message");
}
else
{
Console.WriteLine("\n\n..................................1");
Console.WriteLine("\n\nserverToken.token==>" + serverToken.token);
Console.WriteLine("\n\nserverToken.pk==>" + serverToken.pk);
}
},
ex =>
{
MessageBox.Show("Exception = " + ex.Message);
},
() =>
{
Console.WriteLine("End of Process.. Releaseing all Resources used.");
});
}
}`
Here's couple options:
You could replace the Reactive Extensions model with a simpler async/await -model for the web requests using HttpClient (you also need Microsoft.Bcl.Async in WP7). With HttpClient your code would end up looking like this:
Request and Response:
public static async Task<string> postAndGetResponse(String postData)
{
if (GlobalConstants.isGetToken)
{
//Pre Request for every reusest
await getServerTokenMethod();
}
var client = new HttpClient();
var postMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(Constants.SERVICE_URI));
var postResult = await client.SendAsync(postMessage);
var stringResult = await postResult.Content.ReadAsStringAsync();
return stringResult;
}
Viewmodel:
public class ListExampleViewModel
{
SecurityToken sToken = null;
public ListExampleViewModel()
{
GetData();
}
public async void GetData()
{
GlobalConstants.isGetToken = true;
var listResults = await REQ_RESP.postAndGetResponse("postData");
}
}
Another option is, if you want to continue using Reactive Extensions, to look at RX's Concat-method. With it you could chain the token request and the actual web request: https://stackoverflow.com/a/6754558/66988

Resources