oauth2 in core 3.1 - asp.net-core

I am using core 3.1 to connect to the canvas API, this is part of my code..
services.AddAuthentication(config =>
{
config.DefaultAuthenticateScheme = "CanvasCookies";
config.DefaultSignInScheme = "CanvasCookies";
config.DefaultChallengeScheme = "CanvasLMS";
})
.AddCookie("CanvasCookies")
.AddOAuth("CanvasLMS", config =>
{
var canvas_domain = Configuration.GetValue<string>("Canvas:Domain");
var client_secret = Configuration.GetValue<string>("Canvas:Secret");
var client_id = Configuration.GetValue<string>("Canvas:Client_id");
config.ClientId = client_id;
config.ClientSecret = client_secret;
config.CallbackPath = new PathString("/oauth/callback");
//config.Scope.Add("google.com")
config.AuthorizationEndpoint = $"{canvas_domain}login/oauth2/auth";
config.TokenEndpoint = $"{canvas_domain}login/oauth2/token";
config.UserInformationEndpoint = $"{canvas_domain}api/v1/users//courses";
config.SaveTokens = true;
config.Events = new OAuthEvents()
{
OnCreatingTicket = context =>
{
var accessToken = context.AccessToken;
var base64payload = accessToken.Split('.')[1];
var bytes = Convert.FromBase64String(base64payload);
var jsonPayload = Encoding.UTF8.GetString(bytes);
var claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonPayload);
foreach(var claim in claims)
{
context.Identity.AddClaim(new Claim(claim.Key, claim.Value));
}
return Task.CompletedTask;
}
this is the controller
public class APICanvasController : Controller
{
...
[Authorize]
public async Task<IActionResult> Secret()
{
var serverResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
var apiResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
return View();
}
private async Task<HttpResponseMessage> SecuredGetRequest(string url)
{
var token = await HttpContext.GetTokenAsync("access_token");
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
return await client.GetAsync(url);
}
public async Task<HttpResponseMessage> AccessTokenRefreshWrapper(
Func<Task<HttpResponseMessage>> initialRequest)
{
var response = await initialRequest();
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await RefreshAccessToken();
response = await initialRequest();
}
return response;
}
private async Task RefreshAccessToken()
{
...
}
}
}
when I execute the code I obtain this error
Exception: The oauth state was missing or invalid.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
Any idea what I am doing wrong?
Thanks

CallbackPath is not supposed to refer to a controller, it refers to a unique path handled by the auth middleware. It will redirect back to your controller when it's done.
"/oauth/callback" should handle oauth authentication result as a json instead of page.

Related

Authorize using JWT token still returning unauthorized

my startup.cs (asp.net core 5.0)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication (options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "https://www.yogihosting.com",
ValidIssuer = "https://www.yogihosting.com",
ClockSkew = TimeSpan.Zero,// It forces tokens to expire exactly at token expiration time instead of 5 minutes later
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007"))
};
});
}
I am trying to invoke http://localhost:31254/Reservation which returns the list of flight reservation lists which I am tring to call from CallAPIController.cs
ReservationController.cs
[Authorize]
public IEnumerable<Reservation> Index()
{
return CreateDummyReservations();
}
[HttpGet]
public IEnumerable<Reservation> Get() => CreateDummyReservations();
public List<Reservation> CreateDummyReservations()
{
List<Reservation> rList = new List<Reservation> {
new Reservation { Id=1, Name = "Ankit", StartLocation = "New York", EndLocation="Beijing" },
new Reservation { Id=2, Name = "Bobby", StartLocation = "New Jersey", EndLocation="Boston" },
new Reservation { Id=3, Name = "Jacky", StartLocation = "London", EndLocation="Paris" }
};
return rList;
}
CallAPIController.cs
//entry point of the controller
public async Task<IActionResult> Index(string message)
{
ViewBag.Message = message;
var accessToken = GenerateJSONWebToken(); //generating the token
SetJWTCookie(accessToken); //setting the cookie
List<Reservation> list = await FlightReservation();
return RedirectToAction("Home");
}
private string GenerateJSONWebToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "https://www.yogihosting.com",
audience: "https://www.yogihosting.com",
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private void SetJWTCookie(string token)
{
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddHours(3),
};
Response.Cookies.Append("jwtCookie", token, cookieOptions);
}
public async Task<List<Reservation>> FlightReservation()
{
var jwt = Request.Cookies["jwtCookie"];
List<Reservation> reservationList = new List<Reservation>();
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
//calling the reservation controller asynchronously but returning
unauthorised.
using (var response = await httpClient.GetAsync("http://localhost:31254/Reservation")) // change API URL to yours
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string apiResponse = await response.Content.ReadAsStringAsync();
reservationList = JsonConvert.DeserializeObject<List<Reservation>>(apiResponse);
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
//return RedirectToAction("Index", new { message = "Please Login again" });
}
}
}
return reservationList;
}
When I call the reservation controller I get 401 unauthorised error, though. If i remove authorize attribute it works. The point is it is not recognising the JWT token or validating it properly. Am I missing anything?
Your problem is in this line :
SetJWTCookie(accessToken);
List<Reservation> list = await FlightReservation();
When you set the cookie, the response must be received in the browser to set the cookie in the browser to be sent in subsequent requests, but the response has not been sent to the browser yet and you call this method
await FlightReservation();
This method requests a cookie that has not yet been set in the browser and has not been sent to this request, so the received token is empty here
var jwt = Request.Cookies["jwtCookie"]; //cookie is null
And the unauthorized error returns, but if this request ends, there will be no problem in subsequent requests because the cookie is set in the browser. So with your code it will always return unauthoruzed in first Request.
But if all requests fail, see if you have set up UseAuthentication Middleware or not
app.UseRouting();
app.UseAuthentication(); //here
app.UseAuthorization();
You set the cookie in your SetJWTCookie(string token) method:
Response.Cookies.Append("jwtCookie", token, cookieOptions)
And you try to get the cookie in the FlightReservation() method:
var jwt = Request.Cookies["jwtCookie"];
your Request dosn't contain jwtCookie. so you can't get the value of your jwt
token
Just try as below:
[HttpPost]
public async Task<IActionResult> Authenticate(UserModel someuser)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "https://www.yogihosting.com",
audience: "https://www.yogihosting.com",
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials
);
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
var response = await httpClient.GetAsync(url); // change API URL to yours
}
return Content(jwtToken);
}
Result:

Identity server identity token is always null

I'm using identity server with .net core application.
Current scenario: Getting token from Facebook, authenticating the user using Facebook API. And then creating a user in our database.
Everything is working fine, except that the returned token always have null identity_token.
I've tried to add id_token token to the scopes but didn't work also.
Login API:
[HttpPost("SocialLogin")]
public async Task<IActionResult> SocialMediaLogin()
{
try
{
var protocol = _httpContextAccessor.HttpContext.Request.Scheme;
var host = _httpContextAccessor.HttpContext.Request.Host.Value;
var baseUrl = $"{protocol}://{host}/";
#region Validating Headers and Parameters
StringValues jwtToken = Request.Headers["token"];
if (!jwtToken.Any())
{
return BadRequest("Each user should have a token");
}
var secretKey = _configuration["SecretKey"].ToString();
var (status, paylod) = GeneralUtilities.CheckSocialMediaTokenHeaderAndValues(jwtToken, secretKey);
if (status != ResponseStatus.Success)
{
return BadRequest("User Id or token are wrong");
}
#endregion
var clientId = _configuration["Clients:Mobile:Id"].ToString();
var secret = _configuration["Clients:Mobile:Secret"].ToString();
var consumerKey = _configuration["ConsumerKey"].ToString();
var consumerKeySecret = _configuration["ConsumerKeySecret"].ToString();
var disco = await DiscoveryClient.GetAsync(baseUrl);
if (disco.IsError) throw new Exception(disco.Error);
var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, secret);
var payload = new
{
provider = paylod.Provider,
external_token = paylod.Token,
email = paylod.Email,
profilePicture = paylod.ProfilePictureUrl,
twitter_secret = paylod.TwitterSecret,
consumerKeySecret,
consumerKey,
displayName = paylod.DisplayName
};
//If user exist, we should update the profile Url of the user
var user = await _userManager.FindByEmailAsync(payload.email);
if (user != null)
{
user.ProfilePictureUrl = payload.profilePicture;
await _userManager.UpdateAsync(user);
}
var result = await tokenClient.RequestCustomGrantAsync(grantType: "external", scope: "my-api offline_access", extra: payload);
if (result.IsError) return new JsonResult(result.Json);
if (!string.IsNullOrWhiteSpace(result.AccessToken))
{
return Ok(result);
}
return new JsonResult(null);
}
catch (Exception)
{
return BadRequest();
}
}
As for the Startup.cs here's what I'm doing for Identity:
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
.AddOperationalStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString, sqloptions => sqloptions.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString, sqloptions => sqloptions.MigrationsAssembly(migrationsAssembly)))
.AddAspNetIdentity<AppUser>()
.AddDeveloperSigningCredential();
// Configure Identity
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
});
Where does the identity_token or access_token are built?

INVALID_TICKET Receiving From CAS in Asp.Net Core2.2

I have to use SSO with CAS Login. When I put user name and password then it goes to CAS server for validate the credential, after a successful validation I find out Authentication is true and now I want to validate receive ticket and need some information which comes in XML from CAS.
But when I send the ticket to serviceValidate URL, then every time I receive INVALID_TICKET Error.
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationFailure code='INVALID_TICKET'>
Ticket 'ST-48062-BNWXlqUWFg97PF4UZZKw-cas.identity.ucsb.edu' not recognized
</cas:authenticationFailure>
</cas:serviceResponse>
Please help me. I am providing my code Please check and let me know where I am wrong.
Waiting for favorable response.
In startup's ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddScoped<iUserService, UserService>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.LoginPath = new PathString("/login");
o.AccessDeniedPath = new PathString("/access-denied");
o.Cookie = new CookieBuilder
{
Name = ".AspNetCore.CasiEval"
};
o.Events = new CookieAuthenticationEvents
{
OnSigningIn = async context =>
{
var username = context.Principal.Identity.Name;
var userSvc = context.HttpContext.RequestServices.GetRequiredService<iUserService>();
var ticket_val = context.HttpContext.Request.Query["ticket"].ToString();
var state_val= context.HttpContext.Request.Query["state"].ToString();
string serviceUrl = $"https%3A%2F%2Flocalhost%3A44357%2FHome%2FIndex%3F";
string baseUrl = string.Empty;
baseUrl = "https://cas.application.com/cas/serviceValidate?";
string casUrl = $"{baseUrl}service={serviceUrl}&ticket={ticket_val}";
using (var httpClient = new HttpClient())
{
// Define Headers
httpClient.DefaultRequestHeaders.Accept.Clear();
// Add an Accept header for JSON format.
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.GetAsync(casUrl);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = response.Content.ReadAsStringAsync();
}
}
ClaimsIdentity identity = new ClaimsIdentity();
context.Principal.AddIdentity(identity);
await Task.Delay(100);
return;// Task.FromResult(0);
}
};
})
.AddCAS(o =>
{
o.CasServerUrlBase = Configuration["CasBaseUrl"]; // Set in `appsettings.json` file.
o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.SaveTokens = true;
o.CallbackPath = new PathString("/Home/Index");
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

KnockoutJS + WebAPI 2 Token Authentication - maintain login state until token expires

I'm fairly new to token based authentication and I have a problem of how to maintain login state after I login.
I want to create a SPA website for which I am using Knockoutjs for my front end and SammyJS for routing and changing the views.
After I login in and get the token I store it in localStorage and set the username into an observable which I am displaying.
My problem is that after I close the tab or browser and I go back to the site, the token is in the localStorage but I can't see the user logged in.
I want to maintain the login state until the token expires. My question is what should I do with the token from the localStorage when I enter the site in order to maintain the login state of that user?
Do I need to make something in the startup class or to check if that user exists in the DB?
Thanks in advance!
Here is my code:
StartupAuth.cs
[assembly: OwinStartup(typeof(EventHub.PL.WebUI.Startup))] namespace EventHub.PL.WebUI {
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get;private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public const string TokenEndpointPath = "/api/token";
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// 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
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(TokenEndpointPath),
Provider = new ApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
//app.UseOAuthBearerTokens( OAuthOptions );
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginUser model)
{
var request = HttpContext.Current.Request;
var tokenServiceUrl = request.Url.GetLeftPart(UriPartial.Authority) + request.ApplicationPath + "/api/Token";
using (var client = new HttpClient())
{
var requestParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password)
};
var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
var tokenServiceResponse = await client.PostAsync(tokenServiceUrl, requestParamsFormUrlEncoded);
var responseString = await tokenServiceResponse.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<TokenResponse>(responseString);
var responseCode = tokenServiceResponse.StatusCode;
if (responseCode == HttpStatusCode.OK)
{
RegisterUser user = userRepository.GetNameById(json.Id);
var data = new
{
status = "success",
json.access_token,
user.Lastname
};
return Json(data);
}
return Json(new { status = "failed" });
}
}
here is the KO part:
var LoginApp = function () {
var instance = this;
instance.mainViewModel = new MainViewModel();
instance.loginViewModel = new LoginViewModel();
instance.loginRepository = new LoginRepository();
instance.loginViewModel.signIn = function() {
$('.loader-header').show();
var postData = {
email: instance.loginViewModel.email(),
password: instance.loginViewModel.password
}
instance.loginRepository.SignIn(SignInSuccess, postData);
};
instance.SignInSuccess = function(response) {
if (response.status === 'success') {
instance.mainViewModel.username(response.Lastname);
instance.mainViewModel.isVisible(true);
var userData = {
token: response.access_token,
username: response.Lastname
};
localStorage.setItem('AuthorizationData', JSON.stringify(userData));
$('.loader-header').hide();
dialog.close();
} else {
$('.loader-header').hide();
}
};
instance.init = function () {
ko.applyBindings(instance.loginViewModel, document.getElementById("signin-form"));
ko.applyBindings(instance.mainViewModel, document.getElementById("main-wrapper"));
}
instance.init();
}
$(document).ready(function () {
var loginApp = LoginApp();
});
UPDATE
here is my routing also
var appRoot = root;
(function ($) {
var app = $.sammy('#page', function () {
this.get('#/home', function (context) {
document.title = 'Home - ' + title;
var url = getUrlFromHash(context.path);
loadView(url, new MainViewModel(), MainApp);
//context.load(url).swap();
});
this.get('#/about', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
this.get('#/manage', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
});
$(function () {
app.run('#/home');
});
})(jQuery);
function loadView(url, viewModel, callback) {
$.get(url, function (response) {
var $container = $('#page');
//var $view = $('#page').html(response);
$container.html(response);
callback();
});
}
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = 'home';
return url;
}
Right now all you're doing is storing the user's credentials in localStorage but not using them to perform authorization. One alternative is to use the Sammy.OAuth2 plugin (which you can find it here).
You can define a route to make the authentication like:
app.post("#/oauth/login", function(context) {
this.load('http://yourwebsite/login',
{
cache: false,
type: 'post',
data: {
email: $("input[name=email]").val(),
password: $("input[name=password]").val()
}
})
.then(function(content) {
if(content != false){
if(app.getAccessToken() == null){
app.setAccessToken(token());
}
}else{
app.trigger("oauth.denied");
return false;
}
});
});
In 'protected' routes you can check if the user is already logged in like this:
app.get("#/profile", function(context) {
if(app.getAccessToken() != null)
context.render('view/profile.template');
else
this.requireOAuth();
});
This examples will have to be modified to populate the token according to your scenario. Here's a complete tutorial on Sammy.Oath2.

Microsoft Owin UseJwt

I am having a difficult time using UseJwtBearerAuthentication Method, I am using Microsoft Azure ACS to obtain a token (using a service identity). The JWT token returns fine to my test program. In the test program the token is sent to a MVC WebAPI 2. (The WAAD authentication works fine when token is obtained from Azure Active Directory)
public partial class Startup
{
private const string Issuer = "https://bluebeam-us-east.accesscontrol.windows.net/";
public void ConfigureAuth(IAppBuilder app)
{
string CertificateThumbprint = "99B25E3E31FCD24F669C260A743FBD508D21FE30";
var audience = ConfigurationManager.AppSettings["ida:Audience"];
app.UseErrorPage(new ErrorPageOptions()
{
ShowEnvironment = true,
ShowCookies = false,
ShowSourceCode = true,
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = audience ,
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new X509CertificateSecurityTokenProvider(Issuer, X509CertificateHelper.FindByThumbprint(StoreName.My,StoreLocation.LocalMachine,CertificateThumbprint).First())
},
});
}
The Code to get Token from ACS is as follows:
private async void GetJwtToken()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(IdP.Authority);
var content = new FormUrlEncodedContent(new Dictionary<String, String>
{
{"grant_type","client_credentials"},
{"client_id", IdP.UserName},
{"client_secret", IdP.Password},
{"scope", IdP.Resource}
});
var response = await client.PostAsync("v2/OAuth2-13", content);
response.EnsureSuccessStatusCode();
var jwtdata = await response.Content.ReadAsStringAsync();
var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
AccessToken = jwt.access_token;
TokenType = jwt.token_type;
long expire;
if (long.TryParse(jwt.expires_in, out expire))
{
ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
}
Authorization = AccessToken;
}
}
catch (HttpRequestException re)
{
Response = re.Message;
}
}
The code to request a Resource (WebAPI):
private async void WebApiRequestCall()
{
try
{
ConfigureSsl();
using (var client = new HttpClient())
{
client.BaseAddress = _baseAddress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (!String.IsNullOrWhiteSpace(Authorization))
client.DefaultRequestHeaders.Add("Authorization", Authorization);
var response = await client.GetAsync(WebApiRequest);
response.EnsureSuccessStatusCode();
Response = await response.Content.ReadAsStringAsync();
}
}
catch (HttpRequestException e)
{
Response = e.Message;
}
}
The decoded Token (using google token decoder looks as follows)
Header
{
"x5t": "mbJePjH80k9mnCYKdD-9UI0h_jA",
"alg": "RS256",
"typ": "JWT"
}
Claims
{
"identityprovider": "https://bluebeam-us-east.accesscontrol.windows.net/",
"iss": "https://bluebeam-us-east.accesscontrol.windows.net/",
"http://schemas.microsoft.com/identity/claims/identityprovider": "revu",
"exp": 1406957036,
"nbf": 1406956676,
"aud": "https://bluebeam.com/Bluebeam.Licensing.WebApi/"
}
So I have the following questions:
1) Is using JwtBearerToken the correct method to use to decode decode JWT token from ACS
2) Is there any tracing facilities in Owin that can provide whats going on in the authentication pipeline?
I am using Microsoft Own 3.0-rc1.
It seems that I had an error in my code where I was not setting the correct "bearer header" for OWIN when sending the client request to WebAPI.
After Receiving the JWT Token from ACS, I needed to set the Authorization correctly
private async void GetJwtToken()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(IdP.Authority);
var content = new FormUrlEncodedContent(new Dictionary<String, String>
{
{"grant_type","client_credentials"},
{"client_id", IdP.UserName},
{"client_secret", IdP.Password},
{"scope", IdP.Resource}
});
var response = await client.PostAsync("v2/OAuth2-13", content);
response.EnsureSuccessStatusCode();
var jwtdata = await response.Content.ReadAsStringAsync();
var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
IdP.AccessToken = jwt.access_token;
IdP.TokenType = jwt.token_type;
long expire;
if (long.TryParse(jwt.expires_in, out expire))
{
IdP.ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
}
// Ensure that Correct Authorization Header for Owin
Authorization = String.Format("{0} {1}", "Bearer", IdP.AccessToken);**
}
}
catch (HttpRequestException re)
{
Response = re.Message;
}
}
We also need support for symmetric key on the WebAPI, based upon how ACS sends the token
public void ConfigureAuth(IAppBuilder app)
{
var thumbPrint = ConfigurationManager.AppSettings["ida:Thumbprint"];
var audience = ConfigurationManager.AppSettings["ida:Audience"];
var trustedTokenPolicyKey = ConfigurationManager.AppSettings["ida:SymmetricKey"];
app.UseErrorPage(new ErrorPageOptions()
{
ShowEnvironment = true,
ShowCookies = false,
ShowSourceCode = true,
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions()
{
AllowedAudiences = new[] {audience},
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new X509CertificateSecurityTokenProvider(Issuer,
X509CertificateHelper.FindByThumbprint(StoreName.My, StoreLocation.LocalMachine, thumbPrint)
.First()),
new SymmetricKeyIssuerSecurityTokenProvider(Issuer, trustedTokenPolicyKey),
},
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = audience,
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
}