I have a single, embedded orbeon instance, behind a spring gateway with JWT security. This works well, for the orbeon builder, I need to forward the Authorization header to make the things working properly.
But for the form-runner, now I have a requirement: for some forms the authentication is required, for some others it is not.
It is possible to configure the Orbeon in the following way: if the Authorization header is present, forward it, if not, do nothing (just load the form and let it fill)?
This is the settings for header forwarding in my properties-local-prod.xml
<property
as="xs:string"
name="oxf.http.forward-headers"
value="Authorization"/>
<property
as="xs:string"
name="oxf.xforms.forward-submission-headers"
value="Authorization token allowDraft"/>
<property
as="xs:string"
name="oxf.fr.authentication.method"
value="header"/>
At now I got the default Orbeon unauthorized page.
I got 401 when I am trying to open the embedded from-runner without authentication (obviously there is nothing to send towards to orbeon). I found this in orbeon log:
HTTP status code 401 {controller: "oxf:/apps/fr/page-flow.xml", method: "GET", path:
"/fr/app/form/edit/myforminstanceid", status-code: "401"}
A possible solution would be if it would be possible to define some kind of run context that can be applied per form definition basis, like prod-auth, prod-public (unfortunately these should be active parallelly as we have a single Orbeon instance). (Further thinking the already available run modes, e.g. for the same set of forms I should be able to define that the save-draft is visible for autenticated users, but not for the anonymous users... although I have already solved this with some not-that-nice javascript magic.)
Orbeon itself does not do anything when one put a similar config to the properties-prod.xml, just forwards the given headers.
Albeit the default Orbeon Forms logging could be better (about the further HTTP calls to be aware what is happening in the background), there was that single line in the log what it did when the request arrived. As it turned out, my custom backend (persistence layer) expected the Authorization header.
I had to put to my spring security config a (/contextRoot/orbeon).permitAll() and it started to work.
Related
I have an existing site which I want to use APIM with and I have successfully mapped my APIs across to APIM. However, I have a swagger page which I just want to be available in the same domain as APIM. How can I do this? The swagger page is:
https://mysite.azurewebsites.net/api/swagger/index.html
I want this to come across as something like
https://myapidomain.com/swagger
My apis in APIM are of the form so I want the swagger to match the same domain
https://myapidomain.com/api/myfirstapi
This can be done with a few caveats depending on how complex your web page is:
Create an API with URL suffix of "swagger"
Optionally uncheck "Require subscription" from that API to make it anonymous
Create inner operation with URL template of "/" and GET method
Add policies to operation inbound:
<set-backend-service base-url="https://mysite.azurewebsites.net" />
<rewrite-uri template="/api/swagger/index.html" />
Test and adjust
That will take care of the page itself. There will be problems if page uses cookies or references external scripts/images via relative URIs. Cookies will be a problem because they'll come with domain set to "mysite.azurewebsites.net", so you need to take care of this in policies.
Resources with relative URLs are problem because browser will make additional requests to your APIM service with those URLs, so you'll need to create additional operations to cover those requests. Using * in operation template might help cover multiple resources.
I have two spring-boot processes. I have Spring Security enabled on both, and I'm using Spring Security OAuth2 SSO setup. I'm also using Eureka and Zuul to allows calls into Boot1 to call into services in Boot2. UI is using Angular with REST calls into the services, and the token being used is a Json Web Token.
This all seems to work, certainly in the UI. All the requests use the Authorization header (which contains the JWT) and the spring security filter in the services successfully parse the JWT and extracts the Security Context from it. As part of the Spring Web processing, it adds a JSESSIONID value to the client's cookie.
Recently, I only had Spring security on Boot1. When calling rest services into Boot1, which end up using Zuul to forward requests to Boot2, all I required in the rest client was to include the Authorization header with the JWT and it all worked fine.
However, I have recently added Spring Security to Boot2 (using the #EnableResourceServer annotation) and now rest calls fail unless I have both the Authorization header as well as a Cookie header that contains a JSESSIONID value. Calls don't fail, but they return empty values.
I've enabled logging to Spring Security, and it validates all correctly in Boot1. It's going into the same ZuulFilter. But there's no activity on Boot2.
Is there something in Zuul that requires a JSESSIONID value to be defined in order for it forward the request? Or is this in Boot2, where it is expecting a JSESSIONID header value due to the introduction of Spring Security filters?
--- update ---
I've stepped through boot1. From what I can see, code in the OAuth2TokenRelayFilter is throwing an exception. Specifically, the method getAccessToken is calling restTemplate.getAccessToken().getValue (line 90, version 1.1.0-RELEASE) which throws a UserRedirectRequiredException.
So, while the TokenRelayFilter has a token, it's attempting to refresh it. When it receives an exception, it's throwing a BadCredentialsException instead of using what's already been defined.
--- update 2 ---
Putting a breakpoint in OAuth2RestOperationsConfiguration, making rest calls without the JSESSIONID always ends up with a new DefaultOAuth2ClientContext to be created, as it's trying to create session-scoped beans. With the JSESSIONID, it's using a persisted DefaultOAuth2ClientContext, which will have the context.
So, is it possible to, when constructing the DefaultOAuth2ClientContext, to see if the request contains the token and uses it? Or something like this? We're trying to move to stateless services, and this seems to be a hurdle towards this.
This turned out to be an issue with the client-id values used by the different parts of the system.
Looking at OAuth2TokenRelayFilter, it is attempting to refresh the token if the client-id defined for the resource server (boot1) matches that which is defined as part of the token contained within the token provided with the request. In my case, this was true: the token was defined using the same client-id.
That really is not correct. When I update my rest client to use a token, but using a different client-id when requesting the token, then the request is forwarded correctly as expected, without the need for a jsessionid. This is exactly what I want.
I suspect that this was caused, in the end, by incorrect use of client-id values by the components of my system.
I am trying to provide security to the REST endpoints. I am following instructions from this page. In my case I don't have view hence I haven't created controller to specify the views and haven't added viewResolver in my AppConfig.java
After implementation it correctly shows the access denied error upon calling a secured REST endpoint. But even though I specify username/password in the request header I get the access denied error. I am testing in postman setting username/password in Basic Auth. What am I missing any idea?
The example you have followed is implementing a form-based authentication. In order to change it to http auth (which is more suitable for REST services) you need to look for the following form-login tag in your security.xml:
<form-login
login-page="/login"
default-target-url="/welcome"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
And just change it to an empty http-basic tag:
<http-basic />
If you did not change anything else, then it supposed to work perfectly. You can also test your setup from your browser, by trying to access your page. If you configured everything properly you will get a popup this time, not a form. That will be HTTP-basic authentication welcoming you.
Since likely you are using the Java-based configuration, the equivalent of this change would be to replace:
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')")
.and().formLogin();
with:
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')")
.and().httpBasic();
How does the new routing service deal with security? According to http://blogs.microsoft.co.il/blogs/applisec/archive/2011/12/12/wcf-routing-and-message-security.aspx, it might be difficult when default windows security is not chosen (typically a simple username/password scenario).
Can wcf routing actually support a scenario where the router receives a WS-Security secured message over HTTP and forwards it to another server over HTTP, without unwrapping the security token?
My scenario is as follows:
A server (relying party), a custom STS with username/password authentication and a client. We use ws2007FederationHttpBinding and message security.
Now we setup wcf routing, it works with basicHttp or wsHttp.
Then we using WIF, we can instanciate proxies, the STS generates claims, but it fails at the first service call. It seems the router is waiting for the certificate definition (included, otherwise we get an error), then seems to require Cardspace UI (while in fact we're using username/password).
If so, would you have an example ?
Thanks.
Good question, i couldn't find anything about this on google yet beside this question also being unanswered on msdn. I don't think this is added out of the box as normally u would need to use delegatation (ActAs) whenever u want to route the request to another service.
The only solution i can think of is creating a message inspector and use that one in your WCF Routing Service. And ofcourse u'll need to use "SupportInteractive = false"
I did found something that might be the answer, see the following post (ignore silverlight lol) http://zamd.net/2011/02/08/silverlight-claim-based-security/
Zamd says:
For the 2nd part I have implemented a message inspector along with an extension method which makes it super easy to attach the SAML with outgoing messages.
I have a problem with the anti CRSF MVC mechanism. The cookie and the form input returned does not match. I'm getting an error every single time, only in one specific page. In the rest of the application it works well.
The server is returning HTTP 500 Internal Server Error and I can see on the log this exception:
[System.Web.Mvc.HttpAntiForgeryException]: {"A required anti-forgery
token was not supplied or was invalid."}
This is the hidden input that the server is generating is:
<input name="__RequestVerificationToken" type="hidden" value="QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2+ZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io/0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld">
And this is the Cookie returned:
Set-Cookie:__RequestVerificationToken_L2VGbG93=skmTAVI8HCbfxDS+xhioIMIISL3UOBI7qJM1JbHjTtAqKl4W70pDUcTKMm0p3R3mrHDziE8vXw0C0OO4HArzWO1/e6py+v/cFdbe9maFgjl4jMiZ9Wc4YIhC6+IUXkk6yqJDJ8dCIr8qtGaYcD9IX+m7/SlVhu521KQSWJYRcaY=; path=/; HttpOnly
When I examine what the server is sending, the cookie is exactly the same, but the payload has different encoding I think:
__RequestVerificationToken:QK8P7rjyZE6Vm5seY7Fr704YCOoFGdTIMzl1W7R0ZFpXSMjGKLG2T05DfFSYTxvtQCEx7DDT69DGsDB2%2BZXFHY8oAjiKz0gw8BhDFywgmfIpoXnGpj7fONNzIIfvbrDrE9WJsMu6Io%2F0bDLM5WfKs0zktiNjyOWpfYrmnfINYmjW8NLOZFoz74xTcgTptAld
The differences are in two characters that appear encoded:
/ -> %2F
+ -> %2B
Those are the only differences I can find between the hidden input field, and the post payload.
What could be the problem that is causing that ValidateAntiForgeryToken fails in verify the token?
Regards.
I've had and resolved several issues with ValidateAntiForgeryToken lately, so I'll share my findings with you.
Salt: Since you mention this only happens on a single page, my best guess is that you are using different salt values in your calls to Html.AntiForgeryToken(salt) and ValidateAntiForgeryToken(salt) calls.
AJAX: as another answer has said, using AJAX may require extra work to ensure the token is included in the POST. Here is my favorite simple, automatic solution to add the token to all AJAX POST requests.
In your question though, you state that you have verified that the token is sending. Have you verified that you're only sending the token once? I found out that an AJAX call of mine was sending the token twice, which combined the values, and caused it to fail.
Machine Key and Cookies: this issue is ugly, easy to spot (causes exceptions), but not very intuitive. The validation cookies and tokens are encoded and decoded using a unique "machine key". This means that if you have a server farm, or change your server, your cookie will no longer be valid. Closing your browser fixes the issue (because the cookie is a session cookie). However, some people leave their browser windows open in the background for a long time!
The solution is to set a "machine key" in your config file. This will tell MVC to use the same key on all servers, ensuring that the cookie will be decryptable everywhere.
Encoding Bugs: using a testing utility called jMeter, we attempted to load-test our pages, only to find out that it had a bug that caused our token to have 2 extra " around the value.
The solution is to lower your trust in your tools! Test in a browser, and if that works, create a test that extracts the token and cookie values, and set a breakpoint to verify the results.
If none of these things work for you, then I'd recommend taking a look at the MVC source code for ValidateAntiForgeryTokenAttribute, specifically the OnAuthorization method. It will help you see the different steps where validation could fail. You might even inspect your error's Exception.StackTrace to determine which part is failing.
As a side note, I really dislike the implementation of ValidateAntiForgeryToken in MVC, because:
There are about 5 verification steps that can fail, but there is only one generic error message.
The class is sealed, so it cannot be extended with additional functionality.
The encryption method is weird - it initializes a Page and creates an artificial ViewState to encrypt the tokens and cookies. Seems overkill.
So, I grabbed the source code, and created my own specialized subclass, which also turned out to be very helpful in debugging its issues, because I could set breakpoints on the validation methods, and it was really easy to determine which validation step was failing.
If this is being sent as an Ajax request, then the current setup of the framework isn't build to do this naturally.
Luckly Phil Haak wrote a nice blog post on dealing with CSRF and Ajax -> Preventing CSRF With Ajax which goes into some good detail about how to use the existing framework and modify it to work for Ajax/Json.
From my recent findings ...
If you set content type as "application/x-www-form-urlencoded" in the ajax request then you must put the AFRT in the data
If you set the content type to "application/json" then the token goes in the ajax "headers" property as described by haack.
On the server if you are checking for the form type token then using the vanilla AntiForgeryRequestTokenAttribute is ok but if you want to validate tokens sent in the header then you need to call the AntiForgeryToken.OnAuthorize ... or whatever, passing the token from the cookie (http context).
It aint easy but if it was everybody would be doing it :)