Spring SPNEGO without form - spring-boot

I have some questions about setting up SPNEGO without use of form fall back.
I am writing a web service that uses SPNEGO authentication and returns a signed JWT for the authenticated principal. I don't have any use for forms (can't use forms with authentication-only principals, for example).
Can I skip the formLogin() and logout() parts as shown in the spring SPNEGO reference, and use the zero-argument SpnegoEntryPoint constructor?
It seems from looking at the source for SPNEGO filter that if the Authorization header is missing then the filter doesn't actually return a 401 Unauthorized response with a WWW-Authenticate: Negotiate challenge to the caller. That is, there is no else to the check on line 135. Am I reading this correctly? The RFC states
If the server receives a request for an access-protected object, and
if an acceptable Authorization header has not been sent, the server
responds with a "401 Unauthorized" status code, and a "WWW-
Authenticate:" header
If the above is correct, are clients expected to preemptively send the Authorization header?
Thanks!
EDIT (and probably RESOLVED): My original security config had:
http.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/v1/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore()...`
When I remove .antMatchers("/v1/**").permitAll() the SpnegoEntryPoint kicks in and I now see a 401 with WWW-Authenticate: Negotiate response.
Of course now I get a Server not found in Kerberos database gss_init_sec_context() failure but i suspect that has to do with SPN setup or host name resolution (since I am testing this locally on my Macbook) than the Spring app.
Will update once finally when I get this working.

Yes, the zero argument cosntructor is correct.
The Filter is not responsible for setting the Status and Header. The SpnegoEntryPoint is. org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint.commence(HttpServletRequest, HttpServletResponse, AuthenticationException)
No, see 2.

Related

Jmeter Digest Authentication

Is it possible to use digest auth in jmeter.
When server answers 401 with auth header
(etc: WWW_Authenticate :SP Digest realm="SD Digest Authentication Realm", qop="auth", nonce="MTYyNTE2Mjc5MDE4NDo0ZTQ0NWJjM2Y0MWQ4OGFlMzQyODRmMjEzNWViMTYwNQ==")
on the first request from client and then client must resend original request with properly formed auth headers?
I try to use HTTP Authorization Manager with http client 4, but no luck.
Jmeter does not send any auth header and does not repeat original request.
Try removing this / from "Domain" section, I don't think it's applicable for Digest authentication, it's more for NTLM and/or Kerberos. But given server sends proper WWW-Authenticate header even given wrong HTTP Authorization Manager configuration you should be seeing the Authorization request header
Apart from this I cannot reproduce your issue using simple Apache web server with mod_auth_digest

quay.io OAuth2 Proxy: Setting Bearer token to Authorization Header

What I want to do
Calling an URL which is proxied by the oauth2 proxy. The oauth2 proxy should perform an authorization code flow in case no authentication is available. In case there is already an authentication available, the access token should be set to the Authorization Header in the request which is forwarded to the upstream.
What I tried
According to the documentation I'd expect that, when setting --pass-authorization-header the token which is requested should be added to the authorization header.
I also experimented with --pass-access-token which should set an X-Forwarded-Access-Token header.
I couldn't see this header at my service either.
Could someone explain to me what I'm doing wrong?
I found the solution.
This post on a github issue lead me to my mistake.
I did misunderstand what the request is and what the response is and how to handle them using nginx ingresses.
If you are using OAuth2-Proxy with a Kubernetes ingress using nginx subrequests (https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/) the data that comes back to nginx is actually an HTTP response, so you will need to use HTTP Response headers (the --pass-* options configure request headers to the upstream).
Try --set-authorization-header and then you need to use this annotation to have the Kubernetes take the subrequest response header and add it to the proxied request header: nginx.ingress.kubernetes.io/auth-response-headers
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#external-authentication

Form Login and HTTP Authorization header

I was checking the HTML code for the default form login page of Spring Security using Spring Boot. Apparently, there are 3 fields in the form (username, password and a hidden CSRF token) and it is a POST request.
I assumed that the HTTP request will not use Authorization header in this case - I have not seen any JavaScript code or something like that which encodes the username:password combination into a Base64 encoded string and puts it into HTTP Authorization header.
But when I checked the Google Chrome Developer tools Inspect, I find that the browser is actually sending the Authorization header with Basic scheme (with username and password Base64 encoded). Also the post body of the HTTP request again contained all the 3 fields (username, password and CSRF token).
How does this happen? Will the browser automatically recognize that this is as a login form and automatically create the Authorization header?
Please find my configuration below:
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.antMatchers("/").permitAll()
.and()
//.httpBasic();
.formLogin()
;
httpSecurity.csrf().disable();
}
Please note that I am testing using Form login.
I couldn't reproduce your issue. It would have been much confusing for me if it did
When I put back httpBasic() in config, as I expected I see WWW-Authenticate: Basic realm="Realm" in the server response, which makes the browser to create the login popup and Send the Authorization header on submission.
Do you see any such response header before the login form is displayed?

Returning HTTP 401 status for AJAX responses without WWW-Authenticate

Is it OK to return an HTTP 401 status for a response to an AJAX call if you wish to convey that the user is not logged in, even though the login mechanism is form-based and not HTTP based (Basic, Digest, etc.)?
The answer here suggests that 401 should be used:
https://stackoverflow.com/a/6937030/2891365
And this post shows an actual example of someone using 401 for an AJAX response: http://www.bennadel.com/blog/2228-some-thoughts-on-handling-401-unauthorized-errors-with-jquery.htm
However, RFC 2616 for HTTP/1.1 clearly states that a special header is necessary, implying that it can only be used for HTTP authentication.
10.4.2 401 Unauthorized
The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource.
I guess I can probably send a bogus header like WWW-Authenticate: WebForm and still conform to W3C specs but it feels like it's violating the spirit of the WWW-Authenticate header.
In the end, I cannot seem to find an authoritative source that explicitly states whether HTTP 401 is allowed for AJAX responses. Is there an authoritative source on this that I missed?
I would say it's not ok since 401 is for telling the client to provide http authentication credentials. The proper response would be 403 Forbidden, simply telling the client it's not allowed to access the resource, for whatever reason.

HTTP response and headers for AJAX/oData authentication?

How oData or AJAX services should respond when the authentication cookie is expired and it's time to renew?
What should the server send to the client when
An oData or AJAX service access is forbidden (access denied)
When the session credentials are stale, and need to be renewed, perhaps by redirecting to an ADFS, OpenID, or Azure ACS IDP
Just looking in Wikipedia lets me guess that I should send some version of 403.x for the first scenario, and a 401 for the second scenario.
Please confirm if the above is correct, and what I should include in the response header and body as well.
Some examples I assume to be incorrect do the following:
Silently error out the AJAX service and return no data
Attempt to redirect the AJAX call to the IDP
Send error text to the client that is not in JSON format
its always safe to play with the HTTP Status codes instead of cooking up your own tokens or anything of that sort.
Since the fundamentals of OData is to make it possible for any client which knows how to communicate HTTP, it makes sense to play around the HTTP status code. The clients will decide what to do on a particular status code.
HTTP Status Codes are the way to go. OData specifically doesn't define anything that is already implemented at a lower level (such as security and authentication.)
401 is for Unauthenticated, 403 is for Unauthorized. For secnario 1 you only say "Access denied" but not why you're denying access. Is the user not authenticated? Then return 401. Is the user authenticated but lacking privileges? Then return 403.
For scenario 2, I would agree, return a detailed 401 status (i.e. with a valid "WWW-Authenticate" header for your authentication provider.)
The Wikipedia article I recommend starting at (you may have already found this) is: https://secure.wikimedia.org/wikipedia/en/wiki/List_of_HTTP_status_codes
Hope this helps someone. :-)

Resources