Can't figure out if OWIN is intercepting requests to api from client - asp.net-web-api

I have an Asp.net webapi with JWT authentication using OWIN middle ware.
My resource server and the authorization server are same. I am able to get the token from the token endpoint. ValidateClientAuthentication and GrantResourceOwnerCredentials methods are hit successfully.
However when I try to access a protected(with [Authorize]) api (with authorization header set to bearer token) I only get "Authorization has been denied for this request".
I have overridden ValidateAuthorizeRequest method just to see if it gets hit when the api call is made via Postman. However it is never hit.
I am trying to figure out a way to see if at all OWIN is intercepting calls to the api other than the calls to the token endpoint.
Is there any way or methods to override so that I can debug and see where in the pipeline the request is being rejected and why.
As of now I make the call via Postman and get an unauthorized response.
Any help would be greatly appreciated.

this is difficult to answer without seeing what you've done.
I am wondering if you have wired things up correctly. Startup class is where you define your Provider and Token format and then you set your application to use those settings. Here is an example:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
int accessTokenExpiresInSeconds = ConfigurationHelper.GetAppSetting("AccessTokenExpirationInSeconds").ToInt();
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(ConfigurationHelper.GetAppSetting("TokenEndPoint")),
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(accessTokenExpiresInSeconds),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(ConfigurationHelper.GetAppSetting("TokenIssuer"))
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
}
}
If that's not the issue then you can use my own article on OAuth2 and JWT, I've got a full example on how to set everything up and the code is on GitHub. Hopefully it will guide you in the right direction:
https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/

Related

Authorization using JWT token in .net framework 4.7 app

I'm working with a .net framework 4.7 app hosted in IIS. The api needs to be secured with JWT token. Identity is provided by another server and clients will send the JWT as bearer token in the header. I like to use OWIN pipeline for authorization. Currently the app uses Global.asax for startup and I like to keep it as is. I just want OWIN for authorization using JWT. I will use the [Authorize] attribute on the controllers needing jwt authorization. IIS doesn't do any authorization at the moment.
I have this in the Startup.cs for Owin.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
How do I call the Startup.Configure() from Global.asax so Owin pipeline handles the authorization for incoming requests.
Thanks
You can have both global.asax and OWIN startup in the same project. ASP.NET will first call the global.asax and hand the control over to OWIN's startup. Make sure you have the Microsoft.Owin.Host.SystemWeb package installed in the project. And you have a class with the name Startup and a method Configuration(IAppBuilder app). There are other ways to let OWIN know where it should start.
You should also be aware of the fact that in .NET framework, there is a manual process to retrieve the signing keys from the authority that issued the JWT token. Otherwise, you will get the mismatched key error. Once you get the keys, you will assign them to ValidSigningKeys property in TokenValidationParameters. Search SO for examples.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}

Solution Spring Backend OAuth2 Client for both web apps as for native (mobile) apps

For the past days I have been trying to figuring out how to make OAuth2 work on a native app with the OAuth2 client consisting of a separate frontend application with a Spring backend. Good news! I figured out a way to make it work both as web app (on a browser) as on a native (mobile) app. Here I would like to share my findings and ask for any suggestions on possible improvements.
Where Spring works out of the box
Spring Oauth2 works out of the box for web apps. We add the dependency <artifactId>spring-security-oauth2-autoconfigure</artifactId>. We add the annotation #EnableOAuth2Client. Furthermore, we add the configuration. For an in detail tutorial I would like to refer you to this tutorial.
Where challenges start to arise
Spring works with a session cookie (JSESSIONID) to establish a session which is send to the frontend using a Set-Cookie header. In a mobile application this Set-Cookie header is not send back to the backend on subsequent requests. This means that on a mobile application the backend sees each request as a new session. To solve this, I implement a session header rather than a cookie. This header can be read and therefore added to the subsequent requests.
#Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
However, that solves only part of the problem. The frontend makes a request using window.location.href which makes it impossible to add custom headers (REST call cannot be used because it would make it impossible to redirect the caller to the authorization server login page, because the browser blocks this). The browser automatically adds cookies to calls made using window.location.href. That's why it works on browser, but not on a mobile application. Therefore, we need to modify Spring's OAuth2 process to be able to receive REST calls rather than a call using window.location.href.
The OAuth2 Client process in Spring
Following the Oauth2 process the frontend makes two calls to the backend:
Using window.location.href a call to be redirected to the Authorization server (e.g. Facebook, Google or your own authorization server).
Making a REST GET request with the code and state query parameter to retrieve an access token.
However, if Spring does not recognise the session (like on mobile phone) it creates a new OAuth2ClientContext class and therefore throws an error on the second call: InvalidRequestException("Possible CSRF detected - state parameter was required but no state could be found"); by the AuthorizationCodeAccessTokenProvider.class. The reason it throws this error is because the preservedState property is null on the request. This is nicely explained by this post's answer of #Nico de wit.
I created a visual of the Spring OAuth2 process which shows the box 'Context present in session?'. This is where it goes wrong as soon as you have retrieved the authorization code from logging into the authorization server. This is because further on in in the getParametersForToken box it checks the preservedState which is then null because it came from a new OAuth2ClientContext object (rather than the same object that was used when redirecting the first call to the page of the authorization server).
The solution
I solved this problem by extending OAuth2ClientContextFilter.class. This class is responsible for redirecting the user to the authorization server login page if no authorization code has been retrieved yet. Instead of redirecting, the custom class now sends back a 200 and the in the body an url to which the frontend needs to be redirected. Also the frontend can now make a REST call rather than using window.location.href to be redirected. That looks something like:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
request.setAttribute(CURRENT_URI, this.calculateCurrentUri(request));
try {
chain.doFilter(servletRequest, servletResponse);
} catch (IOException var9) {
throw var9;
} catch (Exception var10) {
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
UserRedirectRequiredException redirect = (UserRedirectRequiredException)this.throwableAnalyzer.getFirstThrowableOfType(UserRedirectRequiredException.class, causeChain);
if (redirect == null) {
if (var10 instanceof ServletException) {
throw (ServletException)var10;
}
if (var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}
throw new NestedServletException("Unhandled exception", var10);
}
// The original code redirects the caller to the authorization page
// this.redirectUser(redirect, request, response);
// Instead we create the redirect Url from the Exception and add it to the body
String redirectUrl = createRedirectUrl(redirect);
response.setStatus(200);
response.getWriter().write(redirectUrlToJson(redirectUrl));
}
}
The createRedirectUrl contains some logic building the Url:
private String createRedirectUrl(UserRedirectRequiredException e) {
String redirectUri = e.getRedirectUri();
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(redirectUri);
Map<String, String> requestParams = e.getRequestParams();
Iterator it = requestParams.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> param = (Map.Entry)it.next();
builder.queryParam(param.getKey(), param.getValue());
}
if (e.getStateKey() != null) {
builder.queryParam("state", e.getStateKey());
}
return builder.build().encode().toUriString();
}
I hope it helps others in the future by implementing OAuth2 using Spring on web and mobile applications. Feel free to give feedback!
Regards,
Bart

Setting OAuth2 token for RestTemplate in an app that uses both #ResourceServer and #EnableOauth2Sso

On my current project I have an app that has a small graphical piece that users authenticate using SSO, and a portion that is purely API where users authenticate using an Authorization header.
For example:
/ping-other-service is accessed using SSO.
/api/ping-other-service is accessed using a bearer token
Being all cloud native our app communicates with other services that uses the same SSO provider using JWT tokens (UAA), so I figured we'd use OAuth2RestTemplate since according to the documentation it can magically insert the authentication credentials. It does do that for all endpoints that are authenticated using SSO. But when we use an endpoint that is authed through bearer token it doesn't populate the rest template.
My understanding from the documentation is that #EnableOAuth2Client will only extract the token from a SSO login, not auth header?
What I'm seeing
Failed request and what it does:
curl -H "Authorization: Bearer <token>" http://localhost/api/ping-other-service
Internally uses restTemplate to call http://some-other-service/ping which responds 401
Successful request and what it does:
Chrome http://localhost/ping-other-service
Internally uses restTemplate to call http://some-other-service/ping which responds 200
How we worked around it
To work around this I ended up creating the following monstrosity which will extract the token from the OAuth2ClientContext if it isn't available from an authorization header.
#PostMapping(path = "/ping-other-service")
public ResponseEntity ping(#PathVariable String caseId, HttpServletRequest request, RestTemplate restTemplate) {
try {
restTemplate.postForEntity(adapterUrl + "/webhook/ping", getRequest(request), Map.class);
} catch (HttpClientErrorException e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.SERVICE_UNAVAILABLE);
}
return new ResponseEntity(HttpStatus.OK);
}
private HttpEntity<?> getRequest(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getRequestToken(request));
return new HttpEntity<>(null, headers);
}
private String getRequestToken(HttpServletRequest request) {
Authentication token = new BearerTokenExtractor().extract(request);
if (token != null) {
return (String) token.getPrincipal();
} else {
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
if (accessToken != null) {
return accessToken.getValue();
}
}
throw new ResourceNotFound("No valid access token found");
}
In the /api/** resources there is an incoming token, but because you are using JWT the resource server can authenticate without calling out to the auth server, so there is no OAuth2RestTemplate just sitting around waiting for you to re-use the context in the token relay (if you were using UserInfoTokenServices there would be one). You can create one though quite easily, and pull the incoming token out of the SecurityContext. Example:
#Autowired
private OAuth2ProtectedResourceDetails resource;
private OAuth2RestTemplate tokenRelayTemplate(Principal principal) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
details.getTokenValue();
OAuth2ClientContext context = new DefaultOAuth2ClientContext(new DefaultOAuth2AccessToken(details.getTokenValue()));
return new OAuth2RestTemplate(resource, context);
}
You could probably turn that method into #Bean (in #Scope("request")) and inject the template with a #Qualifier if you wanted.
There's some autoconfiguration and a utility class to help with this pattern in Spring Cloud Security, e.g: https://github.com/spring-cloud/spring-cloud-security/blob/master/spring-cloud-security/src/main/java/org/springframework/cloud/security/oauth2/client/AccessTokenContextRelay.java
I came across this problem when developing a Spring resource server, and I needed to pass the OAuth2 token from a request to the restTemplate for a call to a downstream resource server. Both resource servers use the same auth server, and I found Dave's link helpful but I had to dig a bit to find out how to implement this. I ended up finding the documentation here, and it turn's out the implemetation was very simple. I was using #EnableOAuth2Client, so I had to create the restTemplate bean with the injected OAuth2ClientContext and create the appropriate resource details. In my case it was ClientCredentialsResourceDetails. Thanks for all great work Dave!
#Bean
public OAuth2RestOperations restTemplate (OAuth2ClientContext context) {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
// Configure the details here
return new OAuth2RestTemplate(details, context)
}
#Dave Syer
My UAA service is also an oauth2 client, which needs to relay JWT tokens coming in from Zuul. When configuring the oauth2 client the following way
#Configuration
#EnableOAuth2Client
#RibbonClient(name = "downstream")
public class OAuthClientConfiguration {
#Bean
public OAuth2RestTemplate restTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
}
I do get a 401 response from the downstream service as my access token has a very short validity and the AccessTokenContextRelay does not update an incoming access token (Zuul does renew expired access tokens by the refresh token).
The OAuth2RestTemplate#getAccessToken will never acquire a new access token as the isExpired on the access token stored by the AccessTokenContextRelay drops the validity and refresh token information.
How can this by solved?

WebApi with OWIN SelfHost and Windows Authentication

I have a console application SERVER that hosts WebApi controllers using OWIN self-hosting, and runs under a custom account named "ServiceTest1".
In the same machine I have another console application CLIENT that runs under the account "ServiceTest2", and I want to capture in SERVER that "ServiceTest2" invoked a controller action. However:
WindowsIdentity.GetCurrent() is always "ServiceTest1".
Thread.CurrentPrincipal is an unauthenticated GenericIdentity.
RequestContext.Principal is null.
User is null.
What do I need to make this WebApi OWIN self-hosted to grab the Windows identity of the caller?
Your question is a little unclear on exactly how you've implemented the Windows authentication.
Enable Windows authentication:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// ...
}
}
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;

How to configure grails/spring authentication scheme per url?

How can I configure a grails application using Spring security such that one set of url's will redirect unauthenticated users to a custom login form with an http response code of 200, whereas another set of url's are implementing restful web services and must return a 401/not authorized response for unauthenticated clients so the client application can resend the request with a username and password in response to the 401.
My current configuration can handle the first case with the custom login form. However, I need to configure the other type of authentication for the restful interface url's while preserving the current behavior for the human interface.
Thanks!
If I understood right what you want to do, I got the same problem, before! but it is easy to solve it using Spring Security grails Plugin! So, first of all, you have to set your application to use basic authentication:
grails.plugins.springsecurity.useBasicAuth = true
So your restful services will try to login, and if it doesnt work it goes to 401!
This is easy but you also need to use a custom form to login right?! So you can just config some URL to gets into your normal login strategy like this:
grails.plugins.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter',
'/**': 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'
]
So noticed, that above, everything that comes to the URL /api/ will use the Basic Auth, but anything that is not from /api/ uses the normal authentication login form!
EDIT
More information goes to http://burtbeckwith.github.com/grails-spring-security-core/docs/manual/guide/16%20Filters.html
I had the same issue and did not found a good solution for this. I am really looking forward a clean solution (something in the context like multi-tenant).
I ended up manually verifying the status and login-part for the second system, which should not redirect to the login page (so I am not using the "Secured" annotation). I did this using springSecurityService.reauthenticate() (for manually logging in), springSecurityService.isLoggedIn() and manually in each controller for the second system. If he wasn't, I have been redirecting to the specific page.
I do not know, whether this work-around is affordable for your second system.
You should make stateless basic authentication. For that please make following changes in your code.
UrlMappings.groovy
"/api/restLogin"(controller: 'api', action: 'restLogin', parseRequest: true)
Config.groovy
grails.plugin.springsecurity.useBasicAuth = true
grails.plugin.springsecurity.basic.realmName = "Login to My Site"
grails.plugin.springsecurity.filterChain.chainMap = [
'*' : 'statelessSecurityContextPersistenceFilter,logoutFilter,authenticationProcessingFilter,customBasicAuthenticationFilter,securityContextHolderAwareRequestFilter,rememberMeAuthenticationFilter,anonymousAuthenticationFilter,basicExceptionTranslationFilter,filterInvocationInterceptor',
'/api/': 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'
]
resources.groovy
statelessSecurityContextRepository(NullSecurityContextRepository) {}
statelessSecurityContextPersistenceFilter(SecurityContextPersistenceFilter, ref('statelessSecurityContextRepository')) {
}
customBasicAuthenticationEntryPoint(CustomBasicAuthenticationEntryPoint) {
realmName = SpringSecurityUtils.securityConfig.basic.realmName
}
customBasicAuthenticationFilter(BasicAuthenticationFilter, ref('authenticationManager'), ref('customBasicAuthenticationEntryPoint')) {
authenticationDetailsSource = ref('authenticationDetailsSource')
rememberMeServices = ref('rememberMeServices')
credentialsCharset = SpringSecurityUtils.securityConfig.basic.credentialsCharset // 'UTF-8'
}
basicAccessDeniedHandler(AccessDeniedHandlerImpl)
basicRequestCache(NullRequestCache)
basicExceptionTranslationFilter(ExceptionTranslationFilter, ref('customBasicAuthenticationEntryPoint'), ref('basicRequestCache')) {
accessDeniedHandler = ref('basicAccessDeniedHandler')
authenticationTrustResolver = ref('authenticationTrustResolver')
throwableAnalyzer = ref('throwableAnalyzer')
}
CustomBasicAuthenticationEntryPoint.groovy
public class CustomBasicAuthenticationEntryPoint extends
BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
ApiController
#Secured('permitAll')
class ApiController {
def springSecurityService
#Secured("ROLE_USER")
def restLogin() {
User currentUser = springSecurityService.currentUser
println(currentUser.username)
}
}

Resources