I am currently working on authorizing calls to my API controllers. I am following this tutorial: http://www.c-sharpcorner.com/article/asp-net-mvc5-rest-web-api-authorization/
WebApiController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApiAuthorization.Controllers {
[Authorize]
public class WebApiController: ApiController {
// GET api/values
public IEnumerable < string > Get() {
return new string[] {
"Hello REST API",
"I am Authorized"
};
}
// GET api/values/5
public string Get(int id) {
return "Hello Authorized API with ID = " + id;
}
// POST api/values
public void Post([FromBody] string value) {}
// PUT api/values/5
public void Put(int id, [FromBody] string value) {}
// DELETE api/values/5
public void Delete(int id) {}
}
}
AuthorizationHeaderHandler.cs
namespace WebApiAuthorization.Helper_Code.Common {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using WebApiAuthorization.Resources.Constants;
/// <summary>
/// Authorization for web API class.
/// </summary>
public class AuthorizationHeaderHandler: DelegatingHandler {#
region Send method.
/// <summary>
/// Send method.
/// </summary>
/// <param name="request">Request parameter</param>
/// <param name="cancellationToken">Cancellation token parameter</param>
/// <returns>Return HTTP response.</returns>
protected override Task < HttpResponseMessage > SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
// Initialization.
IEnumerable < string > apiKeyHeaderValues = null;
AuthenticationHeaderValue authorization = request.Headers.Authorization; //This is always null even as I put [Authorize] tag above my controller
string userName = null;
string password = null;
// Verification.
if (request.Headers.TryGetValues(ApiInfo.API_KEY_HEADER, out apiKeyHeaderValues) && !string.IsNullOrEmpty(authorization.Parameter)) {
var apiKeyHeaderValue = apiKeyHeaderValues.First();
// Get the auth token
string authToken = authorization.Parameter;
// Decode the token from BASE64
string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
// Extract username and password from decoded token
userName = decodedToken.Substring(0, decodedToken.IndexOf(":"));
password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);
// Verification.
if (apiKeyHeaderValue.Equals(ApiInfo.API_KEY_VALUE) && userName.Equals(ApiInfo.USERNAME_VALUE) && password.Equals(ApiInfo.PASSWORD_VALUE)) {
// Setting
var identity = new GenericIdentity(userName);
SetPrincipal(new GenericPrincipal(identity, null));
}
}
// Info.
return base.SendAsync(request, cancellationToken);
}#
endregion# region Set principal method.
/// <summary>
/// Set principal method.
/// </summary>
/// <param name="principal">Principal parameter</param>
private static void SetPrincipal(IPrincipal principal) {
// setting.
Thread.CurrentPrincipal = principal;
// Verification.
if (HttpContext.Current != null) {
// Setting.
HttpContext.Current.User = principal;
}
}#
endregion
}
}
The problem I am facing is in the SendAsync method:
AuthenticationHeaderValue authorization = request.Headers.Authorization; //This is always null even as I put [Authorize] tag above my controller
Doesn't matter if I put [Authorize], [AllowAnonymous], authorization is always null. Looking for some help. Thanks in advance!
Related
I have a controllerclass called "GebruikersController"
when I use "https://localhost:5001/Gebruikers" I get the right output but when I use "https://localhost:5001/api/GebruikerController/Gebruikers" (how it should work?) I get a blank page. Can anybody help me? Thanks!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using RESTAPI.Data;
using RESTAPI.Data.Repositories;
using RESTAPI.DTOs;
using RESTAPI.Models;
namespace RESTAPI.Controllers
{
[ApiConventionType(typeof(DefaultApiConventions))]
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class GebruikersController : ControllerBase
{
private readonly IGebruikerRepository _gebruikerRepository;
public GebruikersController(IGebruikerRepository context)
{
_gebruikerRepository = context;
}
// GET: api/Gebruikers
/// <summary>
/// Geeft alle gebruikers geordend op achternaam
/// </summary>
/// <returns>array van gebruikers</returns>
[HttpGet("/Gebruikers")]
public IEnumerable<Gebruiker> GetGebruikers()
{
return _gebruikerRepository.GetAlleGebruikers().OrderBy(d => d.Achternaam);
}
// GET: api/Gebruikers/5
/// <summary>
/// Geeft de gebruiker met het gegeven id terug
/// </summary>
/// <param name="id">het id van de gebruiker</param>
/// <returns>De gebruiker</returns>
[HttpGet("{id}")]
public ActionResult<Gebruiker> GetGebruiker(int id)
{
Gebruiker gebruiker = _gebruikerRepository.GetBy(id);
if (gebruiker == null) return NotFound();
return gebruiker;
}
}
}
Try to use
https://localhost:5001/api/Gebruikers/Gebruikers
instead of
https://localhost:5001/api/GebruikerController/Gebruikers.
The "controller" word is not written in the URL.
Add a Route attribute on the action. then, both routes will be recognized.
[HttpGet("/Gebruikers")]
[Route("Gebruikers")]
public IActionResult GetGebruikers()
{
return Ok();
}
The slash will ignore the route attribute on your controller, so it can only recognize the route: "https://localhost:5001/Gebruikers"
In a simple controller action in ASP.NET Core 3.1 that accepts multipart/form-data parameters:
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication
{
public class Controller : ControllerBase
{
[HttpPost("length")]
public IActionResult Post([FromForm] Input input)
{
if (!ModelState.IsValid)
return new BadRequestObjectResult(ModelState);
return Ok(input.File.Length);
}
public class Input
{
[Required]
public IFormFile File { get; set; }
}
}
}
When I send a multipart/form-data with a File with no filename (which is acceptable under RFC 7578) it is not recognized as an IFormFile. For example:
using System;
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;
namespace ConsoleApp4
{
class Program
{
static async Task Main(string[] args)
{
await Test(withFilename: true);
await Test(withFilename: false);
}
static async Task Test(bool withFilename)
{
HttpClient client = new HttpClient();
var fileContent = new StreamContent(new FileStream("/Users/cucho/Desktop/36KB.pdf", FileMode.Open, FileAccess.Read));
var message = new HttpRequestMessage(
HttpMethod.Post,
"http://localhost:5000/length"
);
var content = new MultipartFormDataContent();
if (withFilename)
{
content.Add(fileContent, "File", "36KB.pdf");
}
else
{
content.Add(fileContent, "File");
}
message.Content = content;
var response1 = await client.SendAsync(message);
var withString = withFilename ? "With" : "Without";
Console.WriteLine($"{withString} filename: {(int)response1.StatusCode}");
}
}
}
results in:
With filename: 200
Without filename: 400
How can I bind that File without filename to the IFormFile object?
Edit:
I found in the ContentDispositionHeaderValueIdentityExtensions the following method:
public static class ContentDispositionHeaderValueIdentityExtensions
{
/// <summary>
/// Checks if the content disposition header is a file disposition
/// </summary>
/// <param name="header">The header to check</param>
/// <returns>True if the header is file disposition, false otherwise</returns>
public static bool IsFileDisposition(this ContentDispositionHeaderValue header)
{
if (header == null)
{
throw new ArgumentNullException(nameof(header));
}
return header.DispositionType.Equals("form-data")
&& (!StringSegment.IsNullOrEmpty(header.FileName) || !StringSegment.IsNullOrEmpty(header.FileNameStar));
}
And a similar code in FormFeature, which I think is how it decides whether the part is a file or not.
Just iterating what #Tieson T. mentioned in the comments, model binder requires files to have filenames regardless of their value.
I am sending a request using HttpClient, and this is working. If I omit filename, it won't work.
var formContent = new MultipartFormDataContent();
var streamContent = new StreamContent(request.Stream);
formContent.Add(streamContent, "Photo", "any filename is accepted");
https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FormFileModelBinder.cs#L142
I have to update the list item in sharepoint through Rest Api call,
Requirement:
SharePoint Type: Online
Code for Update: C# Rest API call
Exception: An unhandled exception of type 'System.Net.WebException' occurred in System.dll
Additional information: The remote server returned an error: (403) Forbidden.
public class Class1
{
public static void Main()
{
string result = string.Empty;
Uri uri = new Uri("http://serviceportal.xxx.com/_api/lists/getbytitle('List name')/items(1)");
HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(uri);
wreq.Credentials = CredentialCache.DefaultNetworkCredentials;
wreq.Method = "POST";
wreq.Accept = "application/json; odata=verbose";
wreq.ContentType = "application/json; odata=verbose";
wreq.Headers.Add("X-HTTP-Method", "MERGE");
wreq.Headers.Add("IF-MATCH", "*");
wreq.Headers.Add("X-RequestDigest", null);
string stringData = "{'__metadata': { 'type': 'SP.Data.TestlistListItem' }, 'Title': 'whatever'}";
wreq.ContentLength = stringData.Length;
StreamWriter writer = new StreamWriter(wreq.GetRequestStream());
writer.Write(stringData);
writer.Flush();
WebResponse wresp = wreq.GetResponse();
using (StreamReader sr = new StreamReader(wresp.GetResponseStream()))
{
result = sr.ReadToEnd();
}
}
}
}
How to over come the above exception and is there any code available for update list item in SharePoint using Rest API call kindly share.
You should pass valid SharePoint Online username and password to get the form digest value and use this digest value in your request header instead of null in your code snippet.
Here is a code sample for your reference:
SPHttpClient.cs and SPHttpClientHandler.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace SPORestConsole
{
public class SPHttpClient: HttpClient
{
public SPHttpClient(Uri webUri, string userName, string password) : base(new SPHttpClientHandler(webUri, userName, password))
{
BaseAddress = webUri;
}
/// <summary>
/// Execure request method
/// </summary>
/// <param name="requestUri"></param>
/// <param name="method"></param>
/// <param name="headers"></param>
/// <param name="payload"></param>
/// <returns></returns>
public JObject ExecuteJson<T>(string requestUri, HttpMethod method, IDictionary<string, string> headers, T payload)
{
HttpResponseMessage response;
switch (method.Method)
{
case "POST":
var requestContent = new StringContent(JsonConvert.SerializeObject(payload));
requestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
DefaultRequestHeaders.Add("X-RequestDigest", RequestFormDigest());
if (headers != null)
{
foreach (var header in headers)
{
DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
response = PostAsync(requestUri, requestContent).Result;
break;
case "GET":
response = GetAsync(requestUri).Result;
break;
default:
throw new NotSupportedException(string.Format("Method {0} is not supported", method.Method));
}
response.EnsureSuccessStatusCode();
var responseContent = response.Content.ReadAsStringAsync().Result;
return String.IsNullOrEmpty(responseContent) ? new JObject() : JObject.Parse(responseContent);
}
public JObject ExecuteJson<T>(string requestUri, HttpMethod method, T payload)
{
return ExecuteJson(requestUri, method, null, payload);
}
public JObject ExecuteJson(string requestUri)
{
return ExecuteJson(requestUri, HttpMethod.Get, null, default(string));
}
/// <summary>
/// Request Form Digest
/// </summary>
/// <returns></returns>
public string RequestFormDigest()
{
var endpointUrl = string.Format("{0}/_api/contextinfo", BaseAddress);
var result = this.PostAsync(endpointUrl, new StringContent(string.Empty)).Result;
result.EnsureSuccessStatusCode();
var content = result.Content.ReadAsStringAsync().Result;
var contentJson = JObject.Parse(content);
return contentJson["d"]["GetContextWebInformation"]["FormDigestValue"].ToString();
}
}
}
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
namespace SPORestConsole
{
public class SPHttpClientHandler : HttpClientHandler
{
public SPHttpClientHandler(Uri webUri, string userName, string password)
{
CookieContainer = GetAuthCookies(webUri, userName, password);
FormatType = FormatType.JsonVerbose;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
if (FormatType == FormatType.JsonVerbose)
{
//request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json;odata=verbose"));
request.Headers.Add("Accept", "application/json;odata=verbose");
}
return base.SendAsync(request, cancellationToken);
}
/// <summary>
/// Retrieve SPO Auth Cookies
/// </summary>
/// <param name="webUri"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
private static CookieContainer GetAuthCookies(Uri webUri, string userName, string password)
{
var securePassword = new SecureString();
foreach (var c in password) { securePassword.AppendChar(c); }
var credentials = new SharePointOnlineCredentials(userName, securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
var cookieContainer = new CookieContainer();
cookieContainer.SetCookies(webUri, authCookie);
return cookieContainer;
}
public FormatType FormatType { get; set; }
}
public enum FormatType
{
JsonVerbose,
Xml
}
}
call update list item action like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace SPORestConsole
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri ("https://tenantname.sharepoint.com/sites/dev/");
using (var client = new SPHttpClient(uri, "username#tanantname.onmicrosoft.com", "yourpassword"))
{
var listTitle = "MyList8";
var itemId = 4;
var itemPayload = new { __metadata = new { type = "SP.Data.MyList8ListItem" }, Title = "updateviaRest" };
var endpointUrl = string.Format("{0}/_api/web/lists/getbytitle('{1}')/items({2})", uri, listTitle, itemId);
var headers = new Dictionary<string, string>();
headers["IF-MATCH"] = "*";
headers["X-HTTP-Method"] = "MERGE";
client.ExecuteJson(endpointUrl, HttpMethod.Post, headers, itemPayload);
Console.WriteLine("Task item has been updated");
}
}
}
}
Reference:
Consume SharePoint Online REST service using .NET
I am trying to create an SSO like solution between 2 .Net applications
.Net app 1 has a custom token generator and endpoints to validate tokens that returns user information.
.Net application 2 is protected using Owin and was a typical standalone app and a user would directly login using a password and username.
I created (based on Passion for Coding Blog and Github) a custom Owin provider that would look for a token either in a Authorization header or as a query parameter from a link that a user would click a link from .Net App 1 and send to the .Net App 2 the token in the query string as at GET (I know this is not secure we are eventually going to use OpenID for what it’s worth we just need this for a demo). I am able to get the token validate it and create an Identity and authenticate I just cant get the provider to create a .Net Auth Cookie so that subsequent requests are authenticated and not given a 401 error.
Handler File:
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace SomeOAuth
{
// Created by the factory in the someAuthenticationMiddleware class.
class SomeAuthenticationHandler : AuthenticationHandler<SomeAuthenticationOptions>
{
private const string HandledResponse = "HandledResponse";
private readonly ILogger _logger;
private readonly string _challenge;
/// <summary>
/// Creates a new OpenIdConnectAuthenticationHandler
/// </summary>
/// <param name="logger"></param>
public SomeAuthenticationHandler(ILogger logger, string challenge)
{
_logger = logger;
_challenge = challenge;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
// ASP.Net Identity requires the NameIdentitifer field to be set or it won't
// accept the external login (AuthenticationManagerExtensions.GetExternalLoginInfo)
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization))
{
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
}
}
if (string.IsNullOrEmpty(requestToken))
{
string accessTokenParam = Request.Query.Get("access_token");
if (!string.IsNullOrEmpty(accessTokenParam))
{
requestToken = accessTokenParam;
}
}
if (!string.IsNullOrEmpty(requestToken))
{
using (var client = new HttpClient())
{
try
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://testserver/API/Auth/Authenticate");
var s = new StringContent("{\"oauthtoken\":\"" + requestToken + "\"}", Encoding.UTF8, "application/json");
// var ts = s.ToString();
request.Content = new StringContent("{\"oauthtoken\":\"" + requestToken + "\"}", Encoding.UTF8, "application/json");
System.Diagnostics.Debug.WriteLine("Request:");
System.Diagnostics.Debug.WriteLine(request.ToString());
if (request.Content != null)
{
System.Diagnostics.Debug.WriteLine(await request.Content.ReadAsStringAsync());
}
System.Diagnostics.Debug.WriteLine("");
var response = await client.SendAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
{
return null;
}
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var userId = payload.Value<string>("username");
//need to get the useid of the user as well as the name and role
var identity = new ClaimsIdentity("Some");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "fakeuser", null, "Some"));
/*
identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName + " " + user.LastName));
identity.AddClaim(new Claim(ClaimTypes.Email, user.ContactInfo.Email));
identity.AddClaim(new Claim(ClaimTypes.Sid, user.Guid.ToString()));
*/
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Some"));
AuthenticationProperties properties = CreateProperties("fakeusername", "");
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
return ticket;
}
catch (Exception e)
{
Console.WriteLine("asdf e = " + e.Message);
}
return null;
}
}
else
{
return null;
}
}
/// <summary>
/// Handles SignIn
/// </summary>
/// <returns></returns>
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode == 401)
{
AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null)
{
return null;
}
}
return Task.FromResult<object>(null);
}
public override Task<bool> InvokeAsync()
{
return InvokeReplyPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
{
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket != null)
{
string value;
if (ticket.Properties.Dictionary.TryGetValue(HandledResponse, out value) && value == "true")
{
return true;
}
if (ticket.Identity != null)
{
Request.Context.Authentication.SignIn(ticket.Properties, ticket.Identity);
}
// Redirect back to the original secured resource, if any.
if (!string.IsNullOrWhiteSpace(ticket.Properties.RedirectUri))
{
Response.Redirect(ticket.Properties.RedirectUri);
return true;
}
}
return false;
}
private static AuthenticationTicket GetHandledResponseTicket()
{
return new AuthenticationTicket(null, new AuthenticationProperties(new Dictionary<string, string>() { { HandledResponse, "true" } }));
}
public AuthenticationProperties CreateProperties(string userName, string Roles)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName },
{"roles",Roles}
};
return new AuthenticationProperties(data);
}
}
}
Middleware file:
using Microsoft.Owin;
using Microsoft.Owin.Security.Infrastructure;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.DataHandler;
using Microsoft.Owin.Logging;
namespace SomeOAuth
{
// One instance is created when the application starts.
public class SomeeAuthenticationMiddleware : AuthenticationMiddleware<SomeAuthenticationOptions>
{
private readonly ILogger _logger;
private readonly string _challenge = "Bearer";
public SomeAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, SomeAuthenticationOptions options)
: base(next, options)
{
_logger = app.CreateLogger<SomeAuthenticationMiddleware>();
}
// Called for each request, to create a handler for each request.
protected override AuthenticationHandler<SomeAuthenticationOptions> CreateHandler()
{
return new SomeAuthenticationHandler(_logger, _challenge);
}
}
}
Options file:
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SomeOAuth
{
public class SomeAuthenticationOptions : AuthenticationOptions
{
public SomeAuthenticationOptions(string userName, string userId)
: base(OAuthDefaults.AuthenticationType)
{
UserName = userName;
UserId = userId;
}
public string Challenge { get; set; }
public string UserName { get; set; }
public string UserId { get; set; }
}
}
Extensions File:
using Microsoft.Owin.Extensions;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SomeOAuth
{
public static class SomeAuthenticationExtensions
{
public static IAppBuilder UseSomeeAuthentication(this IAppBuilder app, SomeAuthenticationOptions options)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
app.Use(typeof(SomeAuthenticationMiddleware), app, options);
app.UseStageMarker(PipelineStage.Authenticate);
return app;
}
}
}
Startup File
using System;
using CoreLX.Palms.VS.Web.Services;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin.Security.Providers.OpenID;
using Microsoft.Owin.Security.OAuth;
using Owin;
using SomeOAuth;
using CoreLX.Palms.LS.Web.Common.Models.User;
namespace CoreLX.Palms.VS.Web
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager> (ApplicationSignInManager.Create);
//app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
//{
// AccessTokenProvider = new SomeTokenProvider(),
// Provider = new SomeOAuthBearerAuthenticationProvider("access_token")
//});
app.UseSomeAuthentication(new SomeAuthenticationOptions("testuser", "9"));
// Use a cookie to temp store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = new TimeSpan(0, 3, 0, 0),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
OnApplyRedirect = ctx =>
{
// don't redirect to login page for webapi/ajax requests
// http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/
if (!IsWebApiRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo");
}
private static bool IsWebApiRequest(IOwinRequest request)
{
// hack for check if it's webapi requesr
if (request.Path.StartsWithSegments(new PathString("/api")))
{
return true;
}
// checks if it's ajax request
IReadableStringCollection query = request.Query;
if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IHeaderDictionary headers = request.Headers;
return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}
}
}
I have also tried to just use the custom providers for the standard provided
OAuthBearerAuthenticationProvider
Here is the code for the plugins/providers that I tried I don't have a preference as long as there are no 401 errors:
Provider
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SomeOAuth
{
public class SomeOAuthBearerAuthenticationProvider : IOAuthBearerAuthenticationProvider
{
readonly string _parameterName;
public SomeOAuthBearerAuthenticationProvider(string parameterName)
{
_parameterName = parameterName;
}
public Task ApplyChallenge(OAuthChallengeContext context)
{
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
string token = context.Token;
if(string.IsNullOrEmpty(token) && !string.IsNullOrEmpty(_parameterName))
{
token = context.Request.Query.Get(_parameterName);
}
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
}
}
And the AccessTokenProvider
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json.Linq;
using System;
//using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace SomeOAuth
{
public sealed class SomeTokenProvider : AuthenticationTokenProvider
{
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
using (var client = new HttpClient())
{
try
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://someserver/API/Auth/Authenticate");
var s = new StringContent("{\"oauthtoken\":\"" + context.Token + "\"}", Encoding.UTF8, "application/json");
// var ts = s.ToString();
request.Content = new StringContent("{\"oauthtoken\":\"" + context.Token + "\"}", Encoding.UTF8, "application/json");
System.Diagnostics.Debug.WriteLine("Request:");
System.Diagnostics.Debug.WriteLine(request.ToString());
if (request.Content != null)
{
System.Diagnostics.Debug.WriteLine(await request.Content.ReadAsStringAsync());
}
System.Diagnostics.Debug.WriteLine("");
var response = await client.SendAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
{
return;
}
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var userId = payload.Value<string>("username");
//need to get the useid of the user as well as the name and role
var identity = new ClaimsIdentity("Some");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "someuser", null, "Some"));
/*
identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName + " " + user.LastName));
identity.AddClaim(new Claim(ClaimTypes.Email, user.ContactInfo.Email));
identity.AddClaim(new Claim(ClaimTypes.Sid, user.Guid.ToString()));
*/
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Some"));
context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
catch (Exception e)
{
Console.WriteLine("asdf e = " + e.Message);
}
}
}
}
}
You're registering the middleware in the wrong order. The owin middleware model works through the auth middleware placing an instruction (AuthenticationResponseGrant) in the owin dictionary before returning to the previous middleware. If that previous middleware is the external cookie middleware it will issue a cookie. There's more detail in my blog post. So switch those two lines:
// Use a cookie to temp store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseSomeAuthentication(new SomeAuthenticationOptions("testuser", "9"));
There's also another issue. The AuthenticationType of the identity must mach the one of the cookie middleware. The UseExternalSignInCookie method internally calls app.SetDefaultSignInAsAuthenticationType so when you create the new ClaimsIdentity you shouldn't use Some as authentication type but rather convey the result of app.GetDefaultSignInAsAuthenticationType() through the SignInAsAuthenticationType on the options class. The call to app.GetDefaultSignInAsAuthenticationType() is typically done in the middleware constructor.
I have setup a token authentication process and its working quite well. I am using OWIN.
I am extending 2 specific points which lets me control the signing of the JWT and also the validating of user credentials like so.
Provider = new MyOAuthProvider(),
AccessTokenFormat = new MyJwtFormatter()
How do I hook into the part where the token is being validated. I searched the web and it appears there is a method can't ValidateToken that you can override but I don't know where this is.
I also have the following. Do I need to override something here ?
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders =
new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(
issuer,
secret)
}
});
What might I be missing? Most of the things I have found support what I am doing but not hooking into the token authentication.
I believe its using the internal JWTTokenHandler, I presume you can override this or something?
Here's simple JWT Validation class based on: Google Sign-In for Websites
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Configuration;
using Newtonsoft.Json;
using System.Net;
using System.Threading.Tasks;
using System.Threading;
using Services.Models;
using System.Security.Claims;
namespace Services
{
/// <summary>
/// This is an implementation of Google JWT verification that
/// demonstrates:
/// - JWT validation
/// </summary>
/// #author kunal.bajpai#gmail.com (Kunal Bajpai)
public class CustomJwtHandler : DelegatingHandler
{
string issuer = WebConfigurationManager.AppSettings["GoogleDomain"];
string audience = WebConfigurationManager.AppSettings["GoogleClientId"];
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
var authHeader = request.Headers.Authorization;
if (authHeader == null)
{
// Missing authorization header
return base.SendAsync(request, cancellationToken);
}
if (!TryRetrieveToken(request, out token))
{
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
try
{
ValidateToken(token);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenInvalidAudienceException)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (SecurityTokenValidationException)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception e)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
/// <summary>
/// Validates JWT Token
/// </summary>
/// <param name="token"></param>
private void ValidateToken(string token)
{
try
{
using (WebClient wc = new WebClient())
{
TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(wc.DownloadString("https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + token));
List<Claim> claims = new List<Claim> {
new Claim(ClaimTypes.Name, tokenInfo.Name),
new Claim(ClaimTypes.Email, tokenInfo.Email),
new Claim(ClaimTypes.GivenName, tokenInfo.GivenName),
new Claim(ClaimTypes.Surname, tokenInfo.FamilyName),
};
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, tokenInfo.Issuer));
Thread.CurrentPrincipal = claimsPrincipal;
HttpContext.Current.User = claimsPrincipal;
}
}
catch (WebException e)
{
HttpStatusCode statusCode = ((HttpWebResponse)e.Response).StatusCode;
if (statusCode == HttpStatusCode.BadRequest)
{
throw new SecurityTokenValidationException();
}
else
{
throw new Exception();
}
}
}
/// <summary>
/// Tries to retrieve Token
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authorizationHeaders;
if (!request.Headers.TryGetValues("Authorization", out authorizationHeaders) ||
authorizationHeaders.Count() > 1)
{
return false;
}
var bearerToken = authorizationHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
}
}