Hi all,
After some trouble I have a restservice working with all the stuff like PASOE, oerealm form security, SPA and so on. And it is actually working fine, at least when accessed from a webbrowser. I get the login page and I can login.
However, when I try to access this restservice from a mobile app in Telerik Appbuilder (using the simulator), I can not login, no matter what I do or try. I can see that a .....j_spring_security_check is posted with the right data, but I always get the 401 returned.
Anyone any idea what I can do?
Regards
Kees
Hi Bill,
I created my own login page, actually just like the example you mentioned. If I run the app in the simulator and debug it (using F12 brings up debug screen) I see that the J_spring_security_check is posted but a 401 is returned.
Kees
Can you compare the authenticationModel you used on the browser with the one in mobile. (Are they both using basic or form?). The debugger in chrome can help you compare the successful vs unsuccessful requests.
Other things I have seen are
- http vs HTTPS (where the PASOE instance has different configurations for the two ports)
- CORS issues with simulator (but I don't think you would see an HTTP status of 401 if that were the issue).
The only problem I would think would be the CORSDomain
Does your CORS filter in form-oerealm xml file has the telerik appbuilder domain name in "allowDomains" property
This is the default value, the domain of the client should be added to the list
<b:property name="allowDomains" value="studio.progress.com,http:/.../mobile.progress.com" />
I changed the form-oerealm.xml with these lines (just to be sure) <b:property name="allowAll" value="true" /> <b:property name="allowDomains" value="*" />
Still have the same problem.
I use the Telerik Appbuilder windows client and run the PAS local. I can see the call j_spring_security_check call in the log file localhost_access_log.2016-02-28.txt (returning a 401). I have implemented the Hybridrealm stuff on a differrent Appserver and put some debug messages in it. When accessing the service in the webbrowser I can see these messages in the Appserver log. However, when calling this service from the Telerik Appbuilder i do not get any entries in this Appserver log file. So somehow the call to the service get "stuck somewhere in between".
Hope this will give you guys some more clues. Or maybe some ideas on how to do some more debugging?
Regards
Kees
Hello,
> However, when I try to access this restservice from a mobile app in Telerik Appbuilder (using the simulator), I can not > login, no matter what I do or try. I can see that a .....j_spring_security_check is posted with the right data, but I always get > the 401 returned.
Based on your posts, it seems that you have all the pieces together... login screen in the mobile app, j_spring_security_check is posted indicating that you have set the authenticationModel property to FORM and the right data (username, password) are included in the payload.
Still, the server is returning HTTP error 401 and the calls would not get to the AppServer.
Here are some suggestions for troubleshooting:
- Check the payload of the HTTP response for the j_sprint_security_check post, it may contain details on why the 401 error was returned. Perhaps, the credentials failed.
- Change the authentication method to FORM local to see if the issue is related the setup for OERealm or if there is something else.
I wonder if you would see different results using Telerik Platform ( http://platform.telerik.com ) from a web browser instead of the Windows client.
I hope this helps.
Hi guys,
I think I have found what seems to be the problem. It has something to do with the request header passt by the call from Telerik: Accept: application/json
Doing the call from the webbrowser it passes the header: Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
I can reproduce this by using another rest client and passing the calls, using these two headers.
So now the question is: what have I got to do to fix this? What do I have to change in the configuration files?
Regards
Kees
BTW; changing to form-local authentication method has the same results, so it has (probably) nothing todo with oerealm
keesvlasblom, do you mind sharing your xml file (oeablSecurity-form-oerealm.xml) to take a look?
Hi Kees,
Can you post a capture of the HTTP traffic when your login fails and when it succeeds, from a browser's debugger Network tab or an HTTP monitor like Telerik Fiddler? For example, here is what I see when I reproduce the bug that Peter Judge referred to above, running from the Telerik Platform emulator in a browser:
Do you see a message like this in the console:
XMLHttpRequest cannot load
myhostname:8980/.../home.html
The request was redirected to 'http://myhostname:8980/
myWebApplication /static/auth/login.html', which is disallowed for crossorigin
requests that require preflight.
-- Wayne
Oops, I will try to repost my example.
--Wayne
Appreciate all the help. I am running on 11.6
I do not know how i can attach the xl to this post, so i just copy the full content. I also attach two screenshots, one with the call from telerik Appbuilder and one from the webbrowser(chrome). Hope this will give you some clues.
Regards
Kees
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Application context containing http authentication control
-->
<b:beans xmlns="www.springframework.org/.../security"
xmlns:b="www.springframework.org/.../beans"
xmlns:xsi="www.w3.org/.../XMLSchema-instance"
xsi:schemaLocation="www.springframework.org/.../beans www.springframework.org/.../spring-beans-3.0.xsd
www.springframework.org/.../security www.springframework.org/.../spring-security-3.1.xsd">
<!-- To disable security for APSV, import apsv-none.xml (and comment out import of apsv-basic.xml)-->
<!-- To enable security for APSV, import apsv-basic.xml (and comment out import of apsv-none.xml) -->
<b:import resource="apsv-none.xml"/>
<!--<b:import resource="apsv-basic.xml"/> -->
<!-- To disable security for SOAP, import apsv-none.xml (and comment out import of soap-basic.xml)-->
<!-- To enable security for SOAP, import apsv-basic.xml (and comment out import of soap-none.xml) -->
<b:import resource="soap-none.xml"/>
<!--<b:import resource="soap-basic-local.xml"/> -->
<!-- This HTTP security space represents the REST service and controls
the authentication/authorization process to its dynamic/static content.
ALTER THIS SECTION TO MEET YOUR PRODUCTION DEPLOYMENT REQUIREMENTS
-->
<http pattern="/rest/**"
auto-config="false"
use-expressions="true"
create-session="ifRequired"
disable-url-rewriting="true"
authentication-manager-ref="oeablApplicationAuth"
entry-point-ref="OEFormEntryPoint"
realm="REST Realm" >
<!-- OpenEdge PRE AUTH Filter -->
<custom-filter position="PRE_AUTH_FILTER"
ref="OEPreauthfilter" />
<!-- OpenEdge ClientPrincipal SSO Filter -->
<custom-filter after="SESSION_MANAGEMENT_FILTER"
ref="OEClientPrincipalFilter" />
<!-- OpenEdge CORS Filter -->
<custom-filter before="SECURITY_CONTEXT_FILTER"
ref="OECORSFilter" />
<!-- URL access controls -->
<!-- HTTP REST/Mobile AppServer service -->
<intercept-url pattern="/rest/**"
access="hasAnyRole('ROLE_PSCUser')"/>
<!-- authentication model -->
<form-login login-page="/static/auth/login.jsp"
login-processing-url="/static/auth/j_spring_security_check"
always-use-default-target="false"
default-target-url="/"
authentication-failure-url="/static/auth/loginfail.html"
authentication-success-handler-ref="OEAuthnSuccessHandler"
authentication-failure-handler-ref="OEAuthnFailureHandler" />
<logout logout-url="/static/auth/j_spring_security_logout"
success-handler-ref="OELogoutSuccessHandler"
invalidate-session="true"
delete-cookies="JSESSIONID" />
<!--
<remember-me />
-->
<!-- error handlers -->
<access-denied-handler ref="OEAccessDeniedHandler" />
<!-- login session controls -->
<session-management session-fixation-protection="none" />
</http>
<!-- This HTTP security space represents the WSPD service and controls
the authentication/authorization process to its dynamic/static content.
ALTER THIS SECTION TO MEET YOUR PRODUCTION DEPLOYMENT REQUIREMENTS
-->
<http pattern="/web/**"
auto-config="false"
use-expressions="true"
create-session="ifRequired"
disable-url-rewriting="true"
authentication-manager-ref="oeablApplicationAuth"
entry-point-ref="OEFormEntryPoint"
realm="REST Realm" >
<!-- OpenEdge PRE AUTH Filter -->
<custom-filter position="PRE_AUTH_FILTER"
ref="OEPreauthfilter" />
<!-- OpenEdge ClientPrincipal SSO Filter -->
<custom-filter after="SESSION_MANAGEMENT_FILTER"
ref="OEClientPrincipalFilter" />
<!-- OpenEdge CORS Filter -->
<custom-filter before="SECURITY_CONTEXT_FILTER"
ref="OECORSFilter" />
<!-- URL access controls -->
<intercept-url pattern="/web/**"
access="hasAnyRole('ROLE_PSCUser')"/>
<!-- authentication model -->
<form-login login-page="/static/auth/login.jsp"
login-processing-url="/static/auth/j_spring_security_check"
always-use-default-target="false"
default-target-url="/"
authentication-failure-url="/static/auth/loginfail.html"
authentication-success-handler-ref="OEAuthnSuccessHandler"
authentication-failure-handler-ref="OEAuthnFailureHandler" />
<logout logout-url="/static/auth/j_spring_security_logout"
success-handler-ref="OELogoutSuccessHandler"
invalidate-session="true"
delete-cookies="JSESSIONID" />
<!--
<remember-me />
-->
<!-- error handlers -->
<access-denied-handler ref="OEAccessDeniedHandler" />
<!-- login session controls -->
<session-management session-fixation-protection="none" />
</http>
<!-- This HTTP security space represents the REST service and controls
the authentication/authorization process to its dynamic/static content.
ALTER THIS SECTION TO MEET YOUR PRODUCTION DEPLOYMENT REQUIREMENTS
-->
<http pattern="/**"
auto-config="false"
use-expressions="true"
create-session="ifRequired"
disable-url-rewriting="true"
authentication-manager-ref="oeablApplicationAuth"
entry-point-ref="OEFormEntryPoint"
realm="OpenEdge" >
<!-- OpenEdge CORS Filter -->
<custom-filter before="SECURITY_CONTEXT_FILTER"
ref="OECORSFilter" />
<!-- URL access controls -->
<!-- Restricted Mobile session class uses this page as part of its
login() operation, protect it so it triggers user
authentication -->
<intercept-url pattern="/static/home.html"
access="hasAnyRole('ROLE_PSCUser')"/>
<!-- Mobile application restrictions section ends here -->
<!-- Restricted web application server-status page that gives
an admin/developer access to some web application information.
Must appear before the the wildcard access controls below -->
<intercept-url pattern="/static/ServerStatus.html" method="GET"
access="hasAnyRole('ROLE_PSCUser','ROLE_PSCAdmin','ROLE_PSCDebug')"/>
<intercept-url pattern="/server/**" method="GET"
access="hasAnyRole('ROLE_PSCAdmin','ROLE_PSCDebug')"/>
<!-- Open access resources -->
<intercept-url pattern="/*" method="GET"
access="permitAll()"/>
<intercept-url pattern="/static/*" method="GET"
access="permitAll()" />
<intercept-url pattern="/static/error/*" method="GET"
access="permitAll()"/>
<intercept-url pattern="/static/images/*"
access="permitAll()" />
<intercept-url pattern="/static/auth/*"
access="permitAll()" />
<!-- Restricted access HTTP static files -->
<intercept-url pattern="/static/**"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.htm*" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.gif" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.jpg" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.css" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.js" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.json" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.asp" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/**/*.inc" method="GET"
access="hasAnyRole('ROLE_PSCUser')"/>
<intercept-url pattern="/*" method="GET"
access="permitAll()"/>
<!-- Best practice - deny anything not explicitly granted. -->
<intercept-url pattern="/**" access="denyAll()"/>
<!-- authentication model -->
<form-login login-page="/static/auth/login.jsp"
login-processing-url="/static/auth/j_spring_security_check"
always-use-default-target="false"
default-target-url="/"
authentication-failure-url="/static/auth/loginfail.html"
authentication-success-handler-ref="OEAuthnSuccessHandler"
authentication-failure-handler-ref="OEAuthnFailureHandler" />
<logout logout-url="/static/auth/j_spring_security_logout"
success-handler-ref="OELogoutSuccessHandler"
invalidate-session="true"
delete-cookies="JSESSIONID" />
<!--
<remember-me />
-->
<!-- error handlers -->
<access-denied-handler ref="OEAccessDeniedHandler" />
<!-- login session controls -->
<session-management session-fixation-protection="none" />
</http>
<!-- The Pre-auth security filter for SSO -->
<b:bean id="OEPreauthfilter"
class="com.progress.appserv.services.security.OERequestHeaderAuthenticationFilter">
<!-- USER-EDIT: To turn on SSO :
1. Set "enabled" property to true
2. For single-domain supply the Domain Access Code as the value for "key" property
3. For multi-domain supply the absolute path of a 'registryFile' generated using OpenEdge's
$DLC/bin/gendomreg.bat utility as "registryFile" property -->
<b:property name="enabled" value="false"/>
<b:property name="key" value=""/>
<!-- <b:property name="registryFile" value= "" /> -->
<b:property name="encoding" value="OECP"/>
<b:property name="headername" value="X-OE-CLIENT-CONTEXT-ID"/>
<b:property name="authenticationManager" ref="authenticationManager" />
</b:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<b:bean id="preauthAuthProvider"
class="com.progress.appserv.services.security.OEPreAuthenticatedAuthenticationProvider">
<!--
<b:property name="rolePrefix" value="ROLE_" />
<b:property name="enabledAttrName" value="ATTR_ENABLED" />
<b:property name="lockedAttrName" value="ATTR_LOCKED" />
<b:property name="expiredAttrName" value="ATTR_EXPIRED" />
-->
</b:bean>
<!-- The security filter that turns a Spring token into an OpenEdge
ClientPrincipal object. The filter is thus responsible for:
1. Creating a ClientPrincipal if one was not created in a previous authentication process step
2. If the previous authentication process produced a Spring token - copy that information into the ClientPrincipal
3. If the ClientPrincipal is not already sealed - seal it using this filter's domain and registry configuration
4. If enablecp is true, then send the ClientPrincipal to the ABL business logic -->
<b:bean id="OEClientPrincipalFilter"
class="com.progress.appserv.services.security.OEClientPrincipalFilter">
<!--
<b:property name="enablecp" value="false" />
<b:property name="key" value="" />
<b:property name="registryFile" value="" />
<b:property name="domain" value="sample" />
<b:property name="roles" value="sample" />
<b:property name="authz" value="false" />
<b:property name="expires" value="600" />
<b:property name="accntinfo" value="true" />
<b:property name="properties" >
<b:map>
<b:entry key="prop-1" value="string1"/>
<b:entry key="prop-2" value="string2"/>
</b:map>
</b:property>
<b:property name="ccid" value="true" />
<b:property name="anonymous" value="true" />
<b:property name="appName" value="OE" />
-->
</b:bean>
<!-- The security filter that implements the CORS standard for controling
cross site resource access by http clients. -->
<b:bean id="OECORSFilter"
class="com.progress.appserv.services.security.OECORSFilter" >
<b:property name="allowAll" value="true" />
<b:property name="allowDomains" value="*" />
<!-- Examples:
<b:property name="allowAll" value="false" />
<b:property name="allowDomains" value="*" />
<b:property name="allowSubdomains" value="false" />
<b:property name="allowMethods" value="" />
<b:property name="messageHeaders" value="" />
<b:property name="responseHeaders" value="" />
<b:property name="supportCredentials" value="true" />
<b:property name="maxAge" value="-1" />
-->
</b:bean>
<!-- Custom form login handlers for JSON requests -->
<b:bean id="OEAuthnSuccessHandler"
class="com.progress.appserv.services.security.OEAuthnSuccessHandler">
<b:property name="acceptType" value="application/json.*" /> <!-- KVL: added line -->
</b:bean>
<b:bean id="OEAuthnFailureHandler"
class="com.progress.appserv.services.security.OEAuthnFailHandler">
<b:property name="acceptType" value="application/json.*" /> <!-- KVL: added line -->
</b:bean>
<b:bean id="OEAccessDeniedHandler"
class="com.progress.appserv.services.security.OEAccessDeniedHandler">
<b:property name="acceptType" value="application/json.*" /> <!-- KVL: added line -->
</b:bean>
<b:bean id="OELogoutSuccessHandler"
class="com.progress.appserv.services.security.OELogoutSuccessHandler">
<b:property name="acceptType" value="application/json.*" /> <!-- KVL: added line -->
</b:bean>
<b:bean id="OEFormEntryPoint"
class="com.progress.appserv.services.security.OEFormAuthnEntryPointHandler">
<b:constructor-arg value="/static/auth/login.jsp" />
<b:property name="acceptType" value="application/json.*" /> <!-- KVL: added line -->
</b:bean>
<!-- Spring AuthenticationProvider that manages the task of authentication
using a user account supplied by an AppServer Realm service. It directly
uses the OERealmUserDetailsImpl bean to do the real work and
create a Spring UserDetails object that will be placed in a Spring
authentication token. -->
<b:bean id="OERealmAuthProvider"
class="com.progress.appserv.services.security.OERealmAuthProvider" >
<b:property name="userDetailsService">
<b:ref bean="OERealmUserDetails"/>
</b:property>
<!--
When createCPAuthn property is true:
will create an OE ClientPrincipal with its additional attributes
that the Spring token does not. This is qualified by the "sealClientPrincipal"
property, which when true will use this bean's domain and registry configuration
to "seal" the ClientPrincipal; and when false will leave the ClientPrincipal
unsealed until the OEClientPrincipalFilter performs the seal operation.
When createCPAuthn property is false
a basic Spring token is generated and passed to the OEClientPrincpial
filter, which will use its configuration to create a ClientPrincipal,
copy the Spring token's information into it, and then seal it using its
domain and access-code configuration. All other properties relating to
ClientPrincpal generation and sealing are ignored
-->
<!--
<b:property name="createCPAuthn" value="true" />
<b:property name="multiTenant" value="false" />
<b:property name="sealClientPrincipal" value="false" />
<b:property name="key" value="oech1::23263c143737253a3337" />
<b:property name="registryFile" value="" />
<b:property name="userDomain" value="" />
<b:property name="authz" value="false" />
<b:property name="properties" >
<b:map>
<b:entry key="prop-1" value="string1"/>
<b:entry key="prop-2" value="string2"/>
</b:map>
</b:property>
<b:property name="expires" value="600" />
-->
</b:bean>
<!-- Spring UserDetailsService that connects to an AppServer Realm service
and uses it as a source of user account information during the
authentication step managed by the OERealmAuthProvider bean.
The sample in this template requires changing the properties
"realmURL" and "realmClass" to contain the run-time AppServer
used for authentication and the developer supplied OOABL class
name. -->
<b:bean id="OERealmUserDetails"
class="com.progress.appserv.services.security.OERealmUserDetailsImpl" >
<!-- KVL:changed <b:property name="realmURL" value="AppServer://localhost:5162/oerealm" /> -->
<b:property name="realmURL" value="AppServer://localhost:5162/ASMoneyVault" />
<b:property name="realmClass" value="OpenEdge.Security.Realm.HybridRealm" />
<b:property name="grantedAuthorities" value="ROLE_PSCUser" />
<b:property name="rolePrefix" value="ROLE_" />
<b:property name="roleAttrName" value="ATTR_ROLES" />
<b:property name="enabledAttrName" value="ATTR_ENABLED" />
<b:property name="lockedAttrName" value="ATTR_LOCKED" />
<b:property name="expiredAttrName" value="ATTR_EXPIRED" />
<b:property name="realmPwdAlg" value="0" />
<!-- KVL:changed <b:property name="realmTokenFile" value="" /> -->
<b:property name="realmTokenFile" value="oespaclient.cp" />
<!-- For SSL connection to the oeRealm appserver provide the complete
path of psccerts.jar as the value of 'certLocation' property
-->
<b:property name="certLocation" value="" />
<!-- set appendRealmError = true in order to append the Realm
class thrown error in the error details send to the REST Client -->
<b:property name="appendRealmError" value="false" />
</b:bean>
<!-- Authentication manager reserved for PUBLIC anonymous authn
to the static and dynaic application content.
-->
<authentication-manager id="oeablApplicationAuth" >
<authentication-provider ref="OERealmAuthProvider" />
</authentication-manager>
</b:beans>
The example where you get the 401 back from j_spring_security_check looks like it may be the bug that Peter mentioned, so his suggested workaround seems worth a try (the bug had to do with the Web application not recognizing the request for application/json because it was looking for application/json.*, I think). In your example from the web browser, where the only thing is a call to j_spring_security_check that returns a 302, does your code use the JSDO library's JSDOSession object? If so, I would expect to see a preliminary request for home.html, and I would expect to see the j_spring_security_check request's Accept header specify application/json.
(One thing is for sure -- you do a better job of posting graphics than I do! ).
Hi,
Sorry that it has taken some time, but I still have not found the reason for this behaviour. After some puzzling it seems there is something wrong with the composing of URL and redirections.
I try to make things clear.
Using the webbrowser I see this behaviour:
http://localhost:8810/MoneyVault/rest/MoneyVaultService/Test -> 302 Found http://localhost:8810/MoneyVault/static/auth/login.jsp -> shows the login page. localhost:8810/.../j_spring_security_check -> 302 found
localhost:8810/.../Test -> 200 OK
In the loggin of the Appserver I can see my debug messages as expected.
When I do a login from my app in the Telerik Appbuilder I get a different behaviour. When the serviceURI is set as http://localhost:8810/MoneyVault/rest I see the following requests:
http://localhost:8810/MoneyVault/rest/static/home.html?_ts=145735507-9774378336-1 -> 401 Not authenticated http://localhost:8810/MoneyVault/rest/static/auth/j_spring_security_check -> 401 Not authenticated and also no-debug messages are found in the logging.
As you can see the difference with the webbrowser call is the …/rest/… part in the URL, caused by the definition of the service URI. So the next thing I tried was to remove the “/rest/” part from the serviceURI. But that is not working at all. The first call is:
http://localhost:8810/MoneyVault/static/home.html?_ts=145736497-8051664180-1 -> 200 Found
Due to the fact that this call gets a response with contenttype text/html (the previous call to home.html returned response with application/json) and that the responsestatus was 200 no authentication was forced and a j_spring_security_check was not called.
So next I tried to change the file progress.all.js. I changed the URL for the spring security check so that the “/rest/” part of the URL was omitted, resulting in call:
http://localhost:8810/MoneyVault/static/auth/j_spring_security_check -> 200 Found
And I also found the desired debug messages in the logging.
But the question is now:
Is this change in progress.all.js needed or do I have to change something in the configuration files or do I have to do something else to make this work?
Regards
Kees
You definitely need to remove "rest" from the serviceURI. I'm sorry I didn't notice that earlier. You should not change the progress.all.js file.
When you removed "rest" from the serviceURI, and got a 200 back on the request for home.html, were you testing from a browser session that you had previously used to make a successful test (where you had entered the
URI directly into the browser)? I am wondering whether the session ID from the successful access was cached, and that allowed you to access home.html. What happens if you do this:
1. remove the change that you made from progress.all.js
2. define the serviceURI without "rest" at the end
3. clear browsing data from the browser you're using with Telerik AppBuilder and restart it (just to be sure)
4. now run the test
--Wayne
When removing the "rest" from the service URI i was testing from the Telerik Appbuilder, running in the simulator. Looking at the progress.all.js file you can see why it does not force a call to j_spring_security_check:
Because the content-type of the response is "text-html" and the response status is 200 it tries to figure out if the response is a login page or a failure page. It does this by searching for the text "j_spring_security_check" in the response. However, the response is just the "home-page" as you can see in the attached screen.
So, I think the response to the home.html is the wrong one, but why?
Below two screen shots, both taken using the simulator/Telerik Appbuilder, with ServiceURI without "rest" part (just "localhost:8810/MoneyVault"). First screenshot shows the headers of the request and response, second one shows the response. Hope this helps.
Seems to be fixed now, do some more testing and will let you know