Basic (username/password) authentication with Auth0 .NetCore Swagger - asp.net-core

I have an API which will be exposed to public which uses Auth0 for Authentication and has swagger enabled to view/document the endpoint.
I have 2 applications configured in Auth0
Web (used to authenticate users who access my UI, this calls the API and works fine. )
API (used to authenticate my API, swagger is currently using this to get a bearer token )
I understand that this is wrong as everyone who comes to Swagger will have access to everything.
So I need to create a controller endpoint /Authentication?username=a&password=b where I can take that username, hash the password and send it to Auth0, get a token and return this to user.
Now user can use this token and Authorise swagger.
I want to let know that this API will be accessed by other third party applications. So I need an endpoint where in they can hit and get a token and make other API calls.
A sample code that I have, which is obviously not working is given below.
[HttpGet]
public async Task<string> Login(string username, string password)
{
var stringContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("client_id", this.Configuration["Auth0:ClientId"]),
new KeyValuePair<string, string>("audience", this.Configuration["Auth0:Audience"]),
new KeyValuePair<string, string>("connection", "Username-Password-Authentication"),
new KeyValuePair<string, string>("scope", "openid profile email"),
new KeyValuePair<string, string>("response_type", "code"),
new KeyValuePair<string, string>("redirect_uri", "https://localhost:5000"),
});
this.client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
dynamic response = await this.client.PostAsync($"{this.Configuration["Auth0:Domain"]}/usernamepassword/login", stringContent).Result.Content.ReadAsStringAsync();
dynamic result = JObject.Parse(response);
return result.access_token;
}
Any help is highly appreciated.

Related

Using httpClient.postasync for web api calls .netcore

I am new to .netcore, I am working on web api that are running on docker container and while using postman the web api's are working really fine outputting the results. I want to make a program in .netcore calling the webapi endpoints and getting the response and using that particular response in other endpoints with MVC.
The explanation is given below.
The default username and password for admin is default set for example username:admin , password: helloworld
. The first time admin login the api requires a new personal password as shown in the Postman figure below.
The login api is: localhost://..../v1/users/login
The first question is How to give the values in Authorization->BasicAuth using .netcore.
The body of the api looks like the figure below.
After setting the new_password the response of the api is a token as given below.
The particular token is then use in the Environment to create user. The image for more clear problem is given below.
Lastly, the token then used to make other API calls such as creating a user.
API: https://localhost/..../v1/users
The image is below.
As a newbie in .netcore language, I am really struggling to do this kind of API calls, as most of the tutorials I tried are generating their own token from API, but here I just want to take the response token and save it and then use it in other API calls.
The StackOverflow community's support was always really handy for me.
The Code I'm trying is given below.
**Controller**
public class Login_AdminController : ControllerBase
{
[Route("/loginAdmin")]
[HttpPost]
public async Task<string> LoginAdminAsync([FromBody] dynamic content)
{
LoginAdmin L = new LoginAdmin();
var client = new HttpClient();
client.BaseAddress = new Uri("https://localhost:9090");
var request = new HttpRequestMessage(HttpMethod.Post, "/v1/users/login");
var byteArray = new UTF8Encoding().GetBytes($"<{L.username}:{L.df_Password}>");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("new_password", "helloWorld123!"));
request.Content = new FormUrlEncodedContent(formData);
var response = await client.SendAsync(request);
Console.WriteLine(response);
return content;
}
}
}
***Model***
public class LoginAdmin
{
public string username = "admin";
public string df_Password = "secret";
public string new_Password { get; set; }
}
Thank you.
Do you want to get token from response? If yes. Try this:
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:12345/Api");
var request = new HttpRequestMessage(HttpMethod.Post, "/token");
var keyValues = new List<KeyValuePair<string, string>>();
keyValues.Add(new KeyValuePair<string, string>("username", "yourusername"));
keyValues.Add(new KeyValuePair<string, string>("password", "yourpassword"));
request.Content = new FormUrlEncodedContent(keyValues);
var response = client.SendAsync(request).Result;
return response.Content.ReadAsStringAsync().Result;
Authorization is handled via the Authorization request header, which will include a token of some sort, prefixed by the scheme. What you're talking about here isn't really basic auth. With that, you literally pass the username and pass in the Authorization header with each request. What you're doing is just authenticating once to get an auth token, and then using that auth token to authorize further requests. In that scenario, you should really be posting the username and pass in the request body. Then, you'd do bearer auth with the token for the other requests, using the Authorization header. Still, to cover both bases:
Basic Auth
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
request.Headers.Add("Authorization", $"Basic {token}");
Bearer Auth
request.Headers.Add("Authorization", $"Bearer {token}");
// where `token` is what was returned from your auth endpoint
FWIW, List<KeyValuePair<string, string>> is just Dictionary<string, string>. It's better to use the real type. Then, you can just do formData.Add("new_password", "helloWorld123!") instead of formData.Add(new KeyValuePair<string, string>("new_password", "helloWorld123!"))

Sending user Id along with access_token

I'm implementing Auth0 in my ASP.NET Core 2.1 app with React front-end.
Once the user authenticates, I get both an access_token and an id_token. I'm clear that the purpose of access_token is to grant access to my API methods. I also understand that the id_token provides user data which I can use in my front-end app.
The question/concern is about sending user data, such as userId to my backend when I make API calls. Other than including userId in the body of my POST request, is there another way to send it to my API method?
Prior to Auth0, I used a couple of other solutions and the JWT token I received from them always included userId, username, etc. I thought this was a more secure approach because even though one can see what's in a JWT token, the signature allows us to make sure the data is not temperered with.
Even though my API calls are secured through SSL, I feel including the userId of the person who's making the API call in the body of my request is less secure compared to sending it through a JWT token.
Am I missing something here or do we indeed send the userId through the regular means in an API call i.e. in the body of a POST call or in the query string of a GET call?
Good question man, i was going through the same problem last week and finally figured it out using the same JWTAccessToken.
The catch is in adding the UserId of the authenticated user as a claim when generating an access token which you can retrieve in the server.
Adding Claims To Access Token
Add the user's id to your list of claims first.
List<Claim> claims = new List<Claim>();
claims.Add(new Claim("UserId", user.Id.ToString()));
Then generate an access token.
SecurityToken token = new JwtSecurityToken(
issuer: {YOUR_ISSUER},
audience: {YOUR_AUDIENCE},
claims: claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(60),
signingCredentials: credentials
);
Am assuming you already know how to perform the steps before reaching this final token generation as deducted from your prowess of oAuth and JWT shown above in your question.
Retrieve Claim From Access Token
To read a UserId from their access_token, let's create a couple of helper/extension methods to help us in reading an access_token from the RequestContext of a controller.
public static string GetUserId(this ControllerBase controller)
{
string securityKey = "{YOUR_SECURITY_KEY}";
SymmetricSecurityKey key = new SymmetricSecurityKey(new UTF8Encoding().GetBytes(securityKey));
JwtSecurityTokenHandler token_handler = new JwtSecurityTokenHandler();
var tokenValidationParams = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateLifetime = false
};
string bearer = controller.HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer", string.Empty).Trim(' ');
List<Claim> claims = token_handler.ValidateToken(bearer, tokenValidationParams, out SecurityToken token).Claims.ToList();
Claim userClaim = claims.FirstOrDefault(x => x.Type == "UserId");
if(userClaim != null)
{
return userClaim.Value;
}
else
{
throw new Exception("Invalid AccessToken. UserId claim not found");
}
}
How To Use
Let's now use this to get the UserId in any of our controllers:
[Authorize]
public class ExampleController : Controller
{
public IActionResult Index()
{
string userId = this.GetUserId();
// --> continuing code goes here.
}
}

How to fetch email in web api 2 which is secured by WAAD

I am using MVC 5 client which is secured by "UseOpenIdConnectAuthentication" and getting all user details in Claims object, this client is calling WAAD secured Web Api by "Bearer" authentication token.
I need to fetch username or email in the web api. I tried different options but nothing worked.
I am getting null in Identity.Name, other properties I am getting like nameidentifier, objectidentifier, tenanted etc.
Please advise.
Thanks
Below code I am using for access token in Web Client.
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
ClientCredential clientcred = new ClientCredential(Startup.clientId, Startup.appKey);
AuthenticationContext authenticationContext = new AuthenticationContext(Startup.aadInstance + Startup.tenantId, new ADALTokenCache(signedInUserID));
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(apiResourceId, clientcred);
return authenticationResult.AccessToken;
Start up Code
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}
Below are the Token Details:
You can get the upn of current user by :
var upn = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn").Value;
Other way is getting user's basic information using Microsoft Graph api , please refer to On-Behalf-Of scenario .The OAuth 2.0 On-Behalf-Of flow serves the use case where an application invokes a service/web API, which in turn needs to call another service/web API. Please refer to protocol explanation and code sample .
Update :
Looking into your codes , you are using client credential flow to acquire token for your web api :
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(apiResourceId, clientcred);
The OAuth 2.0 Client Credentials Grant Flow permits a web service (confidential client) to use its own credentials instead of impersonating a user, to authenticate when calling another web service. That's why you can't get upn information which associated with a user .
You can use authorization code flow with user's identity , Please refer to code sample :
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
result = await authContext.AcquireTokenSilentAsync(todoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

Is it correct to request access token upon EVERY API call in my web app controllers?

I have a web API on the backend, and a web app and phone app on the front end.
I am using Auth0 for OAuth/OpenId authentication on all three.
Both web API and web app are written in ASP .Net Core 1.1 MVC.
Every time I call the web API from the web app, I request an access token from Auth0. So, in every controller action that requires access to my API, I have:
var client = new HttpClient();
var tokenResponse = await client.PostAsync(tokenEndpoint,
new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", _auth0Settings.ClientId),
new KeyValuePair<string, string>("client_secret", _auth0Settings.ClientSecret),
new KeyValuePair<string, string>("audience", _auth0Settings.ApiIdentifier),
}));
Is this the correct way of doing this? Or should I just perform this once when the web app fires up, and then store the access token in a global variable that I can call from all my controllers?
You should call only once and reuse the token. When the token has expired you should request new token with refresh token. If u do not use refresh tokens u should redirect user to login page to reauthenticate or have some sort of auto-reauthenticate mechanism on web app.

How to delegate Identity from Web-Application to WebAPI

I am trying to build a website, where the user logs in at the and can use an backend web-API.
Calls to the backend web-API will always be proxied by the frontend website, since the backend is not publicly available.
Back- and frontend are MVC 6 (or MVC Core?) projects based on ASP.net Core.
The frontend currently authenticates (successfully) by using OpenId-Connect.
The backend should use JwtBearerToken.
The authentication so far requests the response type is id_token code and the scope is openid profile.
After the roundtrip to the Auth-Server (ADFS 2016), I will end up in the AuthorizationCodeReceived-Event from ASP.NET, but I have no luck in exchanging the code for authorization token. I tried the following using ADAL:
public override async Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
await base.AuthorizationCodeReceived(context);
var clientCredential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);
var oAuthContext = new AuthenticationContext(context.Options.Authority, false);
var oAuthResult = await oAuthContext.AcquireTokenByAuthorizationCodeAsync(context.Code, new Uri(context.RedirectUri), clientCredential);
}
I had to disable the authority validation (which I do not like) and I do not get results other than Http-Status 400.
I'd be happy for any advice how to move on.
Update
Further Investigation Shows, that the OpenIdConnect-Configuration allows to save auth and refresh Tokens into the Claims. Nevertheless I don't see the possibility to convert it in the first place.
I also tried exchanging the code by hand (PS: Invoke-WebRequest ...) but had no success. Perhaps this is a problem of ADFS TP4...
I've managed to get this scenario to work with TP4.
AuthorizationCodeReceived = async n =>
{
string code = n.Code;
AuthenticationContext ac = new AuthenticationContext(BaseAddress, false);
ClientCredential client = new ClientCredential("clientid", "secret");
string resourceId = "https://myservices/myapi";
AuthenticationResult ar = await ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("https://localhost:44300/"), client, resourceId);
}
You can then use the access token from a controller method like this:
AuthenticationContext ac = new AuthenticationContext(Startup.BaseAddress, false);
ClientCredential cred = new ClientCredential("clientid", "secret");
string resourceId = "https://myservices/myapi";
AuthenticationResult ar = ac.AcquireTokenSilent(resourceId, cred, UserIdentifier.AnyUser);
var client = new HttpClient();
client.SetBearerToken(ar.AccessToken);
var result = await client.GetStringAsync("http://localhost:2727/identity");