How to properly implement authorization code flow in .NET? - asp.net

I am trying to properly implement Authorization Code flow for a 3rd party REST api for my users to use. However, I am not sure if I am doing it correctly with the redirections. I am storing the Authorization Tokens to Users in the database.
public async Task<IActionResult> Create(int id, string code)
{
var userId = _userManager.GetUserId(User);
var token = _userService.GetById(userId).AccessToken;
var tokenExpiration = _userService.GetById(userId).AccessTokenExpiration;
if (token == null || (DateTime.Now > tokenExpiration))
{
if (code != null)
{
await _trustapApi.GetToken(userId, "http://localhost:58409/Post/Create/" + id, code);
} else
{
var url = await _trustapApi.GetAuthCode("http://localhost:58409/Post/Create/" + id);
return Redirect(url);
}
}
}
This is the code I have at the beginning of all API methods. However, I feel like this is redundant. Are there any tutorials or tips on how to properly implement this flow into a .NET project?

Related

Is there any possibility return the access_token with only user object?

I am implementing a functionality, where access_token will be sent via email, in this case I need to generate this token with a logic to authenticate the user when accessing the link passed via email.
public async Task<IActionResult> GetLink ()
{
var user = await userManager.FindByEmailAsync("eduardo#test.com.br"); // is active user created
if (user != null)
{
var ident = await userManager.GetAuthenticationTokenAsync(user, "Test", "access_token");
return Ok(ident);
}
return NoContent();
}
Based on the research expected would be something like this, but this is not done with persisted data and my model is not allowing this, anyone have any idea how to persist? Or even just return the token?
I think it is a bad behavior not is not acceptable, but, my user dont have a password for access in this case, maybe is necessary using the token or another mode to login.
It is a very simple flow, this link would be one for a granted action (it will only have read access, basically), and this link will be sent only to a user via email.
The above problem can be solved as follows:
[HttpGet("get_token")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(string))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserToken([FromServices] ITokenService TS, [FromServices] IUserClaimsPrincipalFactory<EkyteUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var user = await userManager.FindByIdAsync(User.GetSubjectId());
var IdentityPricipal = await principalFactory.CreateAsync(user);
var IdServerPrincipal = IdentityServerPrincipal.Create(user.Id.ToString(), user.UserName);
Request.Subject = IdServerPrincipal;
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClient());
Request.Resources = new Resources(Config.GetResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = "http://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
It is necessary to identify the user, set the necessary resources and consequently the client that is accessing. After that, just include the access host to generate the token.

Test and use chat bot without emulator

It may seems very basic question but any guidance is appreciated .Where can i start to learn?.
I have used bot framework and made some code that gives total number of cities in particular country .I integrated and used LUIS .All went good but now to test,i use bot emulator of microsoft.
In real time ,i want to use this chat application on my asp.net application .How can i use this code in Asp.net and test without bot emulator.
Working Code(in case it may help someone) -
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Bot.Connector;
using Newtonsoft.Json;
namespace YahooBot
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
string StockRateString;
LUIS StLUIS = await GetEntityFromLUIS(activity.Text);
if (StLUIS.intents.Count() > 0)
{
switch (StLUIS.intents[0].intent)
{
case "StockPrice":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "StockPrice2":
StockRateString = await GetStock(StLUIS.entities[0].entity);
break;
case "Getcity":
StockRateString = await GetCityDetails(StLUIS.entities[0].entity);
break;
default:
StockRateString = "Sorry, I am not getting you...";
break;
}
}
else
{
StockRateString = "Sorry, I am not getting you...";
}
Activity reply = activity.CreateReply(StockRateString);
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private async Task<string> GetStock(string StockSymbol)
{
double? dblStockValue = await YahooBot.GetStockRateAsync(StockSymbol);
if(dblStockValue==null)
{
return string.Format("This \"{0}\" is not an valid stock symbol", StockSymbol);
}
else
{
return string.Format("Stock Price of {0} is {1}", StockSymbol, dblStockValue);
}
}
private async Task<string> GetCityDetails(string citychar)
{
string dblcityValue = await YahooBot.GetCityAsync(citychar);
if (dblcityValue == "")
{
return string.Format("This \"{0}\" is not an valid city ", citychar);
}
else
{
return string.Format("number of cities beginning with {0} is {1}", citychar, dblcityValue);
}
}
private static async Task<LUIS> GetEntityFromLUIS(string Query)
{
Query = Uri.EscapeDataString(Query);
LUIS Data = new LUIS();
using (HttpClient client = new HttpClient())
{
string RequestURI = "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/f2a85791-c9fe-44f8-b23c-2580581dc383?subscription-key=8b4566ea897c4c87960995755aa8881d&verbose=true&timezoneOffset=0&q=" + Query;
HttpResponseMessage msg = await client.GetAsync(RequestURI);
if (msg.IsSuccessStatusCode)
{
var JsonDataResponse = await msg.Content.ReadAsStringAsync();
Data = JsonConvert.DeserializeObject<LUIS>(JsonDataResponse);
}
}
return Data;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
}
A few steps must be followed:
Publish your bot on Azure (can be done from Visual Studio directly), see documentation
Register your bot on dev.botframework.com (see documentation)
Integrate the chat in your Asp.net site, for example using iFrame: see the tutorial on the documentation . There are also other possibilities described here. An important note is that you should use a token instead of secret in the iframe src, see details in the documentation provided before (option 1 vs option 2)
In a nutshell: everything you need is on the official documentation, in the How-to guides

Get Owin IIdentity from IHttpHandler

Accepted answer note:
Although I have appreciated the help of creating my own OwinMiddleware to send images after doing some checks instead of IHttpModule, that doesn't solve the issue entirely.
The thing is I have added an Authorization header to the ajax requests, and inside that header I am sending my Bearer's Token so that I can get logged user information from Owin. So I have to add this header to the image requests either, to be able to get logged user information from image handler middleware.
Original Question:
I am following this blog post to create token based authentication for my web project. Because some resources of my Web API will be used by native mobile clients. And I have heard that token based authentication is the way to go for that. And in my own project I have a custom image request handler. And need the logged user information inside this handler. But when i try to extract user information from ticket I get null. And I am not sure about this but, I think I have 2 different IIdentity objects here, and I need the one stored inside Owin Context.
Here let me show you some codes;
My GrantResourceOwnerCredentials which is storing claims into ClaimsIdentity,
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
....
// checking user credentials and get user information into 'usr' variable
....
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Sid, usr.UserId.ToString()));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userId", usr.UserId.ToString()
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
Helper function to extract user id from the given IIdentity object
public class utils {
public Guid? GetUserIdFromTicket(IIdentity identity)
{
var cId = (ClaimsIdentity)identity;
var uid = cId.FindFirst(ClaimTypes.Sid);
if (uid != null && Comb.IsComb(uid.Value))
return new Guid(uid.Value);
else
return null;
}
....
}
Now I can get the loggedUserId from my controller like,
var loggedUserId = utils.GetUserIdFromTicket(User.Identity);
but if I call it from my IHttpHandler I get null,
public class ImageHandler : IHttpHandler
{
public ImageHandler()
{
}
public ImageHandler(RequestContext requestContext)
{
RequestContext = requestContext;
}
protected RequestContext RequestContext { get; set; }
public utils utils = new utils(); // changed name for simplicity.
public void ProcessRequest(HttpContext context)
{
var strUserId = RequestContext.RouteData.Values["userid"].ToString();
var strContentId = RequestContext.RouteData.Values["contentid"].ToString();
var fileName = RequestContext.RouteData.Values["filename"].ToString();
var size = RequestContext.RouteData.Values["size"].ToString();
var loggedUserId = utils.GetUserIdFromTicket(context.User.Identity);
....
image processing
....
context.Response.End();
}
}
Hope I didn't messed this up for good...
Solution:
I have implemented my own middleware to serv images to my users after doing some checks. Here is my Invoke task implementation. Everything else is just like as recommended in accepted answer. But as stated above, for this to work I have to send images with the Authorization header, or the loggedUserId will be null again.
public async override Task Invoke(IOwinContext context)
{
// need to interrupt image requests having src format : http://www.mywebsite.com/myapp-img/{userid}/{contentId}/{fileName}/{size}/
if (context.Request.Path.HasValue && context.Request.Path.Value.IndexOf("myapp-img") > -1)
{
// get values from url.
var pathValues = context.Request.Path.Value.Split('/');
var strUserId = pathValues[2].ToString();
var strContentId = pathValues[3].ToString();
var fileName = pathValues[4].ToString();
var size = pathValues[5].ToString();
// check if code returned a notfound or unauthorized image as response.
var hasError = false;
// get userId from static utils class providing current owin identity object
var loggedUserId = ChildOnBlogUtils.GetUserIdFromTicket(context.Request.User.Identity);
// save root path of application to provide error images.
var rootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
// assign content type of response to requested file type
context.Response.ContentType = ChildOnBlogUtils.GetContentType(context.Request.Path.Value.ToString());
// if user requested thumbnail send it without doing checks
if (size == "thumb")
{
imgPath = "images/" + strUserId.ToLower() + "/thumbnail/" + fileName;
}
else
{
var canSee = false;
// check if user can see the content and put the result into canSee variable
// I am using loggedUserId inside these checks
...
...
// end checks
if (canSee)
{
// removed some more checks here for simplicity
imgPath = "images/" + strUserId.ToLower() + "/" + fileName;
}
else
{
context.Response.ContentType = "Image/png";
var imgData = File.ReadAllBytes(rootPath + "/images/unauthorized.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
hasError = true;
}
}
if (!hasError) // if no errors have been risen until this point. try to provide the requested image to user.
{
try
{
var imgData = UserMediaContainer.GetFileContent(imgPath); // get file from storage account (azure)
if (imgData.Length == 0)
{
context.Response.ContentType = "Image/png";
imgData = File.ReadAllBytes(rootPath + "/images/notfound.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
else
{
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
}
catch (Exception ex)
{
context.Response.ContentType = "Image/png";
var imgData = File.ReadAllBytes(rootPath + "/images/notfound.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
}
}
else if (context.Request.Path.HasValue && context.Request.Path.Value.IndexOf("profile-img") > -1)
{
// profile image provider. Same code as providing thumbnails.
}
else
{
// if it is not an image request to be handled. move to the next middleware.
await Next.Invoke(context);
}
}
I guess your ImageHandler is processed before everything else in the owin pipeline, which means it is processed before the authorization comes into place.
Since you're using owin I would advise you to drop the IHttpHandler and use some custom owin middleware.
Following this path will allow you to inject your module in the right place in the pipeline.
Creating the middleware is quite easy:
public class ImageProcessingMiddleware : OwinMiddleware
{
public ImageProcessingMiddleware(OwinMiddleware next): base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
string username = context.Request.User.Identity.Name;
Console.WriteLine("Begin Request");
await Next.Invoke(context);
Console.WriteLine("End Request");
}
}
Once you have defined your middleware you can create an extension method for the instantiation:
public static class ImageProcessingExtensions
{
public static IAppBuilder UseImageProcessing(this IAppBuilder app)
{
return app.Use<ImageProcessingMiddleware>();
}
}
Now you can plug-in your middleware in the pipeline:
app.UseImageProcessing();
If you have followed Taiseer sample, you would do that after you have configured the authorization module:
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
Going back to the middleware, you might have noticed there's a method called Invoke:
public async override Task Invoke(IOwinContext context)
{
string username = context.Request.User.Identity.Name;
Console.WriteLine("Begin Request");
await Next.Invoke(context);
Console.WriteLine("End Request");
}
This is the entry-point of each middleware. As you can see I am reading the user's name authorized right after the authorization token has been verified and authorized.
There's an interesting article about owin middleware which is worth reading.

Web API External Bearer Unauthorized

I am trying to call the RegisterExternal method in Web API, after having retrieved a token from facebook. But I keep getting a 401 Unauthorized from my Web API. I am not sure I am correctly implementing the logic flow. My code is;
Ask for supported external login providers;
public async Task<List<ExternalLoginViewModel>> GetExternalLoginsAsync()
{
using (var client = GetNewHttpClient(false))
{
var response = await client.GetAsync("api/account/externalLogins?returnUrl=/&generateState=true");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<List<ExternalLoginViewModel>>();
}
}
From this, I am returned a facebook URL. I follow this and then enter in my facebook username and password. I return back to my app via a deep link and then try and call the RegisterExternal method in the web API like this, passing the facebook "access token" that is returned.
public async Task<bool> SendSubmitRegisterExternalAsync(RegisterExternalBindingModel ro, string accessToken)
{
using (var client = GetNewHttpClient(true))
{
client.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", accessToken));
HttpResponseMessage response = await client.PostAsJsonAsync("api/Account/RegisterExternal", ro);
if (response.IsSuccessStatusCode) return true;
var value = await response.Content.ReadAsStringAsync();
throw new ResponseErrorException(ErrorHelper.GetErrorString(value));
}
}
I receive 'Unauthorized' every time. I do not know what is wrong. My Web API method looks like this, and the class is marked with the [Authorize] attribute.
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
...
I have found three different posts this evening of people asking this exact same question, and in all cases there are no replies, so I am not hopeful but if anyone can shed some light on this it would be great!
EDIT: I have also changed the method signature to 'allowanonymous' and still get unauthorized!
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[AllowAnonymous]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
I have sorted this by not using FacebookSessionClient and doing it via a WebBrowser control instead.
I use the URL from the first step (provided to me by the WebAPI). Then on the Navigated event from the WebBrowser control, i parse the Url for the access token;
public async void ParseUrlForAccessToken(string url)
{
string fieldName = "access_token";
int accessTokenIndex = url.IndexOf(fieldName, StringComparison.Ordinal);
if (accessTokenIndex > -1)
{
int ampersandTokenIndex = url.IndexOf("&", accessTokenIndex, StringComparison.Ordinal);
string tokenField = url.Substring(accessTokenIndex, ampersandTokenIndex - accessTokenIndex);
string token = tokenField.Substring(fieldName.Length);
token = token.Remove(0, 1);
await _dataService.SubmitLoginExternal("Test", token);
}
}
Then as shown above, I call SubmitLoginExternal, which is a call to the following code which uses the access token retrieved from the WebBrowser control Url to register the account (in this case a 'Test' account);
using (var client = GetNewHttpClient(true))
{
client.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", accessToken));
HttpResponseMessage response = await client.PostAsJsonAsync("api/Account/RegisterExternal", ro);
if (response.IsSuccessStatusCode) return true;
var value = await response.Content.ReadAsStringAsync();
throw new ResponseErrorException(ErrorHelper.GetErrorString(value));
}
This has worked and now I have the user registered in my database.
The key was to use a WebBrowser control and not the FacebookSessionClient object or a WebBrowserTask. You cannot use a WebBrowserTask as you need to hook in to the navigated event once the page has loaded to call ParseUrlForAccessToken().

create principle / httpcontextbase.User from users ID

I am using cors and asp.net membership to manage security for an API. However in some cases, the server may need to speak to our api, on behalf of the client. As in UI mvc project code behind > API.
Because the server, not authorised client would be making this call, the asp.net cookie would not be woking or picked up.
I have overloaded AuthorizeCore in order to check to see if there is a token:
if(httpContext.Request.Headers["x-access-token"] != null)
{
// this authorization is from token, rather than cookie
var accessToken = httpContext.Request.Headers["x-access-token"];
var security = new Security();
var decrypted = JsonConvert.DeserializeObject<TokenModel>(security.Decrypt(accessToken));
var clientip = GetClientIp(httpContext.Request);
if(httpContext.Request.Headers["x-client-ip"] == null)
{
return false;
}
if (httpContext.Request.Headers["x-client-ip"] != decrypted.Ip)
{
return false;
}
if (!IsSecretValid(decrypted))
{
return false;
}
MembershipUser u = Membership.GetUser(decrypted.UserId);
}
What I am asking is, How do I convert the membership into a Principle, in order to then let the rest of the code for roles etc to work.
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
Is this even possible, and if so how.
sorry, found a way through deep thought:
httpContext.User = new GenericPrincipal(new GenericIdentity(u.UserName), System.Web.Security.Roles.GetRolesForUser(u.UserName));
This should then enable my controller actions to pick up userid etc if needed.

Resources