Asp.Net 4.x UseJwtBearerAuthentication events onTokenValidated - asp.net-web-api

I have web api project (ASP.Net 4.7) where i am Jwt Bearer Token for authentication and i would like to hook to an event when token is successfully validated call user information endpoint and add custom logic and add claims to identity.
I know we can do something similar with dotnet core https://www.jerriepelser.com/blog/aspnetcore-jwt-saving-bearer-token-as-claim/
Asp.Net 4.x
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(issuer + "/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), new HttpDocumentRetriever());
var discoveryDocument = Task.Run(() => configurationManager.GetConfigurationAsync()).GetAwaiter().GetResult();
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = "Any",
ValidIssuer = issuer,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
{
return discoveryDocument.SigningKeys;
}
},
});
Please advise ?

Related

jwt token in DOTNET WebApi

I literally read the internet through, tried multiple approaches and so on - with no luck.
I am trying to make a pretty small WebApi project with a few controllers protected by a jwt token.
I generate the token using following code:
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("may the force"));
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims.ToArray()),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
I have setup the authentication (in Startup.cs) bits like
services.AddAuthentication(x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("may the force"))
};
});
Using my AuthController I can generate the Token(first code block in this question) - and the Controllers needed to be "protected" is annotated with [Authorize]
If I take the result from AuthController and paste it at https://jwt.io the signature is valid.
I am testing the controllers using postman and setting the request header Authorization to Bearer <the token>
Can anyone point me in the right direction?
Thanks alot!
UPDATE: I misused the jwt.io site initially, thus some of the comments underneath here are no longer valid.
UPDATE 2: Realized that the server tried to use cookie based authentication. Changing the annotation to [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] made the trick - but still not sure why

Is possible to protect scope (web api) and authenticate client (web app mvc) in same project?

Good morning,
I need to have in same project both web api and web app mvc.
Web api has to be protected via bearer token and web app mvc has to be authenticated via identity server.
Is it possible protecting a scope and a client in same project?
I think I have to do something like this in startup
//this to protect scope api1
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:5000/";
options.RequireHttpsMetadata = false;
options.Audience = "api1";
});
//this to authenticate mvc client
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/account/denied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000",
options.RequireHttpsMetadata = false;
options.ResponseType = "id_token token";
options.ClientId = "mvc-implicit";
options.SaveTokens = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("api1");
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
Now, I have to call my Api1 using client_credential with an external client.
But it returns me at login page.
Is it possible to do what I want?
Protected WebApi and Authenticated MVC client in same project?
Now, I have to call my Api1 using client_credential with an external client. But it returns me at login page.
That seems you misunderstand the scenario . Your MVC application is client also is a resource application which protected by Identity Server (in Config.cs):
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
I assume you have api controller in your MVC application :
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET: api/Values
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
And you have config to protect the api actions by using AddJwtBearer :
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:5000/";
options.RequireHttpsMetadata = false;
options.Audience = "api1";
});
That means any request to access the Get action should have an authentication bearer header with access token append , the access token is issued by your Identity Server(endpoint is http://localhost:5000/) and the audience is api1 .
Now your another client could use client credential flow to acquire access token to access your web application :
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
And call your protected actions :
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("http://localhost:64146/api/values");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
So it won't redirect to login page , since client credential in fact is sending HTTP POST request to get access token with app's credential . There is no login page in this scenario .

Double login to MVC and WebAPI

I develop two separated applications: MVC and WebAPI. On some pages of MVC application I perform ajax requests to WebAPI. Furthermore, I use IdentityServer3 as an authentication/authorization framework.
I've already implemented cookie-based authentication for MVC part and token-based for WebAPI basing on tutorials/samples published on GitHub. Each of them works as intended, but user has to log in twice (separately in MVC and WebAPI), which seems to be reasonable because I've used different authentication types.
Is it possible to use IdentityServer3 in a way that user is required to log in once? I'm wondering if it's a good idea to generate access token by MVC app (after cookie-based authorization) and provide it to JavaScript part of application (the token would be used during ajax calls). I think that this solution allows to avoid double signing in. I've read a lot of posts about similar problems, but they haven't given unambiguous answer.
Edit:
I've followed Paul Taylor's suggestion to use "Hybrid Flow" and I've found a couple of samples which illustrate how to implement it (among other things this tutorial), but I cannot figure out how to perform valid ajax requests to WebAPI. Currently, I get 401 Unauthorized error, though HTTP header Authorization: Bearer <access token> is set for all ajax requests.
IdentityServer project
Scopes:
var scopes = new List<Scope>
{
StandardScopes.OfflineAccess,
new Scope
{
Enabled = true,
Name = "roles",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim(IdentityServer3.Core.Constants.ClaimTypes.Role, true)
}
},
new Scope
{
Enabled = true,
DisplayName = "Web API",
Name = "api",
ScopeSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
Claims = new List<ScopeClaim>
{
new ScopeClaim(IdentityServer3.Core.Constants.ClaimTypes.Role, true)
},
Type = ScopeType.Resource
}
};
scopes.AddRange(StandardScopes.All);
Client:
new Client
{
ClientName = "MVC Client",
ClientId = "mvc",
Flow = Flows.Hybrid,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles,
Constants.StandardScopes.Address,
Constants.StandardScopes.OfflineAccess,
"api"
},
RequireConsent = false,
AllowRememberConsent = true,
AccessTokenType = AccessTokenType.Reference,
RedirectUris = new List<string>
{
"http://localhost:48197/"
},
PostLogoutRedirectUris = new List<string>
{
"http://localhost:48197/"
},
AllowAccessTokensViaBrowser = true
}
MVC application project
Startup configuration
const string AuthorityUri = "https://localhost:44311/identity";
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "mvc",
Authority = AuthorityUri,
RedirectUri = "http://localhost:48197/",
ResponseType = "code id_token",
Scope = "openid profile email roles api offline_access",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var tokenClient = new TokenClient(AuthorityUri + "/connect/token", "mvc", "secret");
TokenResponse tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
if (tokenResponse.IsError)
throw new Exception(tokenResponse.Error);
UserInfoClient userInfoClient = new UserInfoClient(AuthorityUri + "/connect/userinfo");
UserInfoResponse userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
ClaimsIdentity id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n => { // more code }
}
});
}
After I receive access token, I store it in the sessionStorage.
#model IEnumerable<System.Security.Claims.Claim>
<script>
sessionStorage.accessToken = '#Model.First(c => c.Type == "access_token").Value';
</script>
Following JavaScript function is used to perform ajax requests:
function ajaxRequest(requestType, url, parameters)
{
var headers = {};
if (sessionStorage.accessToken) {
headers['Authorization'] = 'Bearer ' + sessionStorage.accessToken;
}
$.ajax({
url: url,
method: requestType,
dataType: 'json',
data: parameters,
headers: headers
});
}
WebAPI project
Startup configuration:
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44311/identity",
ClientId = "mvc",
ClientSecret = "secret",
RequiredScopes = new[] { "api", "roles" }
});
Could you tell me what I'm doing wrong?
Edit (solved)
I had invalid configuration of WebAPI because nomenclature is misleading. It turned out that ClientId and ClientSecret should contian name of scope and its secret (link to reported issue).
Following Startup configuration of WebAPI works as intended:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44311/identity",
// It has been changed:
ClientId = "api", // Scope name
ClientSecret = "secret", // Scope secret
RequiredScopes = new[] { "api", "roles" }
});
You need to use IdentityServer3's "Hybrid Flow".
Here's a tutorial on how to implement it with IdentityServer3. https://identityserver.github.io/Documentation/docsv2/overview/mvcGettingStarted.html
This page for an explanation of how the Hybrid Flow works, and how to implement it (using IdentityServer4 - which unlike IdentityServer3, is still actively developed in case you have the option to upgrade). http://docs.identityserver.io/en/release/quickstarts/5_hybrid_and_api_access.html.

asp.net web form client with identity server 4

I have a asp.net solution which consists of
1). asp.net identity server rc 3
2). asp.net Core web api
3). asp.net webform ( not in asp.net core, client)
I don't see any sample with identity server 4 and web form client. Can you please suggest how to authenticate web form user using identity server with asp.net identity and then call api with the access token ?
I don't see identity server 4 sample with web form client or sample
identity server 3 has a sample but it is doing everything in startup
When i see mvc client for identity server 4, it has all settings in configure method and then calls it like this
How will i apply Authorize attribute in webform so that i am redirected to identity server 4 for login and then after login when i call api like this:
how to change client for webform ?
new Client()
{
ClientId = "mvcClient",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = new List<Secret>()
{
new Secret("secret".Sha256())
},
RequireConsent = false;
// where to redirect to after login
RedirectUris = { "http://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:5002" },
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.OfflineAccess.Name,
StandardScopes.Roles.Name,
"API"
}
}
new InMemoryUser()
{
Subject = "1",
Username = "testuser",
Password = "password",
Claims = new List<Claim>()
{
new Claim("name", "Alice"),
new Claim("Website", "http://alice.com"),
new Claim(JwtClaimTypes.Role, "admin")
}
}
return new List<Scope>()
{
StandardScopes.OpenId, // subject id
StandardScopes.Profile, // first name, last name
StandardScopes.OfflineAccess,
StandardScopes.Roles,
new Scope()
{
Name = "API",
Description = "API desc",
Type = ScopeType.Resource,
Emphasize = true,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim(ClaimTypes.Name),
new ScopeClaim(ClaimTypes.Role)
}
}
};
public void CallApiUsingClientCredentials()
{
var tokenClient = new TokenClient("http://localhost:5000/connect/token", "mvc", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var content = await client.GetStringAsync("http://localhost:5001/identity");
var result = JArray.Parse(content).ToString();
}
[Authorize(Roles="admin)]
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
Late answer, but hopefully it helps someone, still supporting web forms.
There is no problem to use startup together with web forms. The only limitation is no place for AuthorizeAttribute there, but it's still not a problem, just put:
app.UseStageMarker(PipelineStage.Authenticate);
at the bottom of your
public void Configuration(IAppBuilder app)
method within OWIN Startup.An example Startup implementation could be fetched from my github. It works with MVC, Web Forms and additionally brings JWT validation from IdentityServer v.3' codebase, upgraded to compile with the latest OWIN libraries.
If I still left anything unclear, don't hesitate to ask in the comments.

How can I see a Web API bearer token before it's returned?

I have my own OAuthAuthorizationServerProvider, but it looks like there's no place to hook into to see the generated bearer token after it's generated.
Is there a way to grab the to-be-returned token before it's returned? (hack or not)
Here's my setup:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
After decompiling, grabbing OAuth source from http://katanaproject.codeplex.com/, and upgrading my NuGet packages to the latest Owin implementation, I can now override a new method in my SimpleAuthorizationServerProvider.
public override Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
string token = context.AccessToken;
return base.TokenEndpointResponse(context);
}
The above will give the ResponseContext to be returned, which contains everything I need: enctypted token, user identity, claims...
This is a new method in the IOAuthAuthorizationServerProvider interface as of NuGet package 3 (I believe)

Resources