Asp net secure web api - c#

I'm trying to find out which is the right way to secure my web api(I am using Sql as database).
Step one: client makes a Login in application.
Client sends username and password.
Asp net checks in sql database if username and password exists.
If exist it sends back a token-key.
In next client's request, do i send again username and password? Or only token?
Also how can i retreive token from asp net and store it inside my asp net application?
Do i need to create a list Collection and add inside the token?
But this way is not thread safe.... Is there any other mechanicm? For stroring-retreiving tokens and other data from asp net application?

You should use JWT Tokens
Here is a useful link for that.
JWT Authentication for Asp.Net Web Api
E.g
Here is how you generate JWT Token
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 20)
{
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}

Related

How to automatically refresh access token in ASP.NET Core MVC app

I have a Web API backend that has an authentication endpoint for retrieving both the access and refresh token. My client already retrieves the access token and creates a new identity to sign in the user, using the HttpContext.
But how do I automatically get a new access token using the refresh token in my ASP.NET MVC client.
I already have the entire backend working for this part.
I've tried adding the Bearer authentication scheme to my client' services.
Now I'm using the Cookie authentication scheme.
Adding the authentication to the service:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => { options.LoginPath = "/Home/Index"; });
And here is how I log the user in:
var token = JsonConvert.DeserializeObject<Token>(result);
AuthenticationProperties options = new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = true,
ExpiresUtc = DateTimeOffset.FromUnixTimeMilliseconds(token.Expiry),
};
// TODO: new claim for the user name
var claims = new[]
{
new Claim(ClaimTypes.Name, dto.Email),
new Claim(ClaimTypes.Role, dto.Role),
new Claim("AccessToken", $"Bearer {token.AccessToken}"),
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
var principal = new ClaimsPrincipal(identity);
HttpContext.SignInAsync(principal, options);
Maybe this isn't the best way to authenticate my client but It worked so far.
I need the back-end and client to be separated because a mobile client also uses the same back-end.
Thanks in advance.

How to validate id_token received by asp.net mvc OWIN from azure AD B2C

We have a security requirement that we must validate the id token we receive from Azure AD B2C. We need to validate these at the minimum
customSecurityLevel, audience, not before and "expiration time", issuer, nonce
Looking asp.net MVC OWIN middleware, I noticed that that OpenIdConnectAuthenicationOptions provides these:
return new OpenIdConnectAuthenticationOptions
{
...
Notifications = new OpenIdConnectAuthenticationNotifications //Specifies events which the OpenIdConnectAuthenticationMiddleware invokes to enable developer control over the authentication process.
{
AuthenticationFailed = this.AuthenticationFailed,
RedirectToIdentityProvider = this.RedirectToIdentityProvider,
AuthorizationCodeReceived = this.OnAuthorizationCodeReceived,
},
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true, // Important to save the token in boostrapcontext
ValidateAudience = true, // Validate the Audience
ValidateIssuer = true, // Validate the Issuer
ValidateLifetime = true, // Validate the tokens lifetime
ValidIssuer = Issuer, // The issuer to be validated
ValidAudience = ClientId, // The Audience to be validated
},
};
Being quiet new to OWIN, I'm trying to understand below:
Does OWIN middleware magically validates the token we receive from Azure AD B2C or do we need to manually perform validation per this:
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapi-manual-jwt-validation/
At what point in time should token validation should occur i.e. AuthorizationCodeReceived event or on redirect controller/action (page) that's configured on Azure AD B2C redirect URL?
We need to validate more attributes that TokenValidationParameters supports e.g. customSecurityAttribute we send on initial payload. Is there a way to extend this?
How do we parse the token that we receive from Azure AD B2C using OWIN?
Any code sample would be handy.
TO make your question simpler.
The idea behind token is to parse the token and get 3 parts from the token
-Header : contain information about in which algorithm the token haven been encrypted
-Payload : information about the user
-Signature: it's the calculation of encryption of ( Header + Payload) using the Azure certificate or( your identity provider).
Next step the user sends request to your back-end along with JWT.
your back-end will parse the token and get certificate type then will preform HTTP request to your identity provider to get certificate
Next your back-end will construct the certificate option and try to do encryption for ( header + Payload) came from your token the output string must be exactly same Signature you received in the token from your front-end.
If every thing is okay
now your back-end will start validating other attributes like Audience, Issuer
if you configure your token to validate the Audience means your front-end required to provide token contain Audience(Application ID) exactly same your back-end as well as for issuer.
the question now how my back-end knows about certificate?
Azure AD using OpenID connect, More information here
since you configred you tenant in backend the auth packge will make a call to https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration to get the details about your identity provider
and one important link as will is ("jwks_uri": "https://login.microsoftonline.com/common/discovery/keys") where the signature hosted.
You can read and search more how to validate certificate JWT and check this
https://codereview.stackexchange.com/questions/70005/authentication-with-jwt
moving to part 2 of validate more attributes.
Since you are using OpenIdConnect, the package has class called OpenIdConnectEvents that you can trigger events and do what ever you want like this
.AddOpenIdConnect(o =>
{
//Additional config snipped
o.Events = new OpenIdConnectEvents
{
OnTokenValidated = async ctx =>
{
//Get user's immutable object id from claims that came from Azure AD
string oid = ctx.Principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
//Get EF context
var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();
//Check is user a super admin
bool isSuperAdmin = await db.SuperAdmins.AnyAsync(a => a.ObjectId == oid);
if (isSuperAdmin)
{
//Add claim if they are
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "superadmin")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
}
};
});
moving to part 3
parsing token in javascript
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(window.atob(base64));
};
parsing token in C#
use this libary https://www.jsonwebtoken.io/
try {
string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
Console.WriteLine(jsonPayload);
} catch (JWT.SignatureVerificationException) {
Console.WriteLine("Invalid token!");
}
or manual
var jwtHandler = new JwtSecurityTokenHandler();
var jwtInput = txtJwtIn.Text;
//Check if readable token (string is in a JWT format)
var readableToken = jwtHandler.CanReadToken(jwtInput);
if(readableToken != true)
{
txtJwtOut.Text = "The token doesn't seem to be in a proper JWT format.";
}
if(readableToken == true)
{
var token = jwtHandler.ReadJwtToken(jwtInput);
//Extract the headers of the JWT
var headers = token.Header;
var jwtHeader = "{";
foreach(var h in headers)
{
jwtHeader += '"' + h.Key + "\":\"" + h.Value + "\",";
}
jwtHeader += "}";
txtJwtOut.Text = "Header:\r\n" + JToken.Parse(jwtHeader).ToString(Formatting.Indented);
//Extract the payload of the JWT
var claims = token.Claims;
var jwtPayload = "{";
foreach(Claim c in claims)
{
jwtPayload += '"' + c.Type + "\":\"" + c.Value + "\",";
}
jwtPayload += "}";
txtJwtOut.Text += "\r\nPayload:\r\n" + JToken.Parse(jwtPayload).ToString(Formatting.Indented);
}
I hope that answer your qustions

Get accesstoken from azure in web with openidconnect

I have a desktop app where I authenticate users via Azure via AuthenticationContext.AcquireTokenAsync.
With the result from this method I can get the access-token, send it to my WCF and in my WCF use JwtSecurityToken / ConfigurationManager< OpenIdConnectConfiguration > to validate the token.
I've implemented login via Azure in a web app now by configuring it with app.UseOpenIdConnectAuthentication. So in my web app I dont explicitly call a method that returns a token. Rather I jack this in in asp.net's flow.
But now I want to fetch the token in a method and send it for validation similiarly how I did in my desktop app. I cannot find any token that the ConfigurationManager accepts however. I've looked in the regular HttpContext and Owincontext but no info that I find there is useful. Is the accesstoken stored anywhere where I can fetch it? Or do I have to do another request to get an accesstoken?
You should be getting access token as part of the response.
A simple way would be to look at the Authorization header. Look at code below -
HttpContext.Current.Request.Headers["Authorization"];
Also, I don't know what you mean by send the token for validation.
If you're trying to validate the token manually, here's a sample that does exactly that -
Manually validating a JWT access token in a web API
In the sample, specifically look at the Global.asax.cs
string jwtToken = null;
AuthenticationHeaderValue authHeader = request.Headers.Authorization;
if (authHeader != null)
{
jwtToken = authHeader.Parameter;
}
if (jwtToken == null)
{
HttpResponseMessage response = this.BuildResponseErrorMessage(HttpStatusCode.Unauthorized);
return response;
}
.........
.........
.........
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters
{
// We accept both the App Id URI and the AppId of this service application
ValidAudiences = new[] { audience, clientId },
// Supports both the Azure AD V1 and V2 endpoint
ValidIssuers = new[] { issuer, $"{issuer}/v2.0" },
IssuerSigningKeys = signingKeys
};
try
{
// Validate token.
SecurityToken validatedToken = new JwtSecurityToken();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);

decrypting JWT to Authentication Ticket with C# JwtFormat.Unprotect

I have a webapi which is authenticated using JWT tokens.
I validate using a custom JWT protection. This is as follows;
private const string AudiencePropertyKey = "as:client_id";
private readonly string _issuer = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
Client client = null;
string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;
if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include the client_id");
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(audienceId);
}
if (client == null) throw new InvalidOperationException("ClientId does not exist.");
string symmetricKeyAsBase64 = client.Secret;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
Access is controlled through a database table, so the user sends across their clientid as a part of the auth service. they are rejected if the clientid doesn't exist in the database, and the ticket is encoded using the secret associated with this db entry and returned to them.
Now I am struggling with the decoding of the JWT on data requests? Now the JWT decodes just fine on jwt.io so I assume there must be someway of decoding using the JwtProtect without requiring a store on the currently dished out JWT tokens? As far as I can see the JwtProtect wants to have the Allowed audiences passed across? (I could so this by returning all from the db but is it really necessary?).
JWT token is just a base64 string, you can freely decode it in multiple ways.
If you want to "unprotect" and validate the ticket you can use System.IdentityModel.Tokens.SecurityTokenHandler.
Check this answer for an example.
BTW, just a personal consideration: the first rule in security is "do not make your own security, but stick with the mass". You will find that following a clear and used path will offer you more support and you will be sure not to mess or to forget something important.

LinqToTwitter Multiple User Authorizer

I have a web application in which any user if opt for social media automation such as twitter, they click on twitter button and a oauth procedure is done. That oauth process is done using php.
I store the oauth token and oauth secret for that particular user in the database.
Now I have four keys:
ConsumerKey // common for all as it is the key of app in dev.twitter.com
ConsumerSecret // common for all as it is the secret of app in dev.twitter.com
OauthToken //store in database, which keeps on changing as other user activates the socia media
OauthTokenSecret //store in database, which keeps on changing
I have applied all the authorizing technique like below:
var auth = new MvcAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = "###################",
ConsumerSecret = "##################",
OAuthToken = token,
AccessToken = secret
}
};
auth.Authorize();
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = "###############",
ConsumerSecret = "#############################",
OAuthToken = token,
AccessToken = secret
}
};
auth.Authorize();
The problem is if I enter the Token and secret which is given on the site dev.twitter.com everything works fine but if I provide the token and secret stored in the database it does not authenticate the user.
On SingleUserAuthorizer, don't call Authorize. Also, any time you provide all 4 credentials to any authorizer, you don't need to call Authoirize. LINQ to Twitter will use those credentials to build the authorization header.
The case where you would call authorize is if you only provided ConsumerKey and ConsumerSecret and the authorizer type is not SingleUserAuthorizer. Authorize implements the part of the OAuth protocol that gets the OAuthToken and AccessToken.
Your syntax for SingleUserAuthorizer should work, but here's another way that matches keys to how the Twitter app page names them:
var auth = new SingleUserAuthorizer
{
Credentials = new SingleUserInMemoryCredentials
{
ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"],
TwitterAccessToken = ConfigurationManager.AppSettings["twitterAccessToken"],
TwitterAccessTokenSecret = ConfigurationManager.AppSettings["twitterAccessTokenSecret"]
}
};

Categories