1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Elytron programmatic login with FORM authentication

Discussão em 'StackOverflow' iniciado por Stack, Abril 23, 2021.

  1. Stack

    Stack Membro Participativo

    we are currently migrating from legacy security subsystem to Elytron and have a Struts2 based web application deployed in JBoss EAP 7.3.6 which should support multiple "flavors" of authentication.

    The standard way of logging in should be that a user manually provides credentials in a login form (j_security_check) and clicks the corresponding button. This works well with Elytron in our setup.

    The second possibility is, that the GET request to protected content of the web application can contain a custom cookie that contains a JWT token. This cookie is intercepted by a io.undertow.server.HttpHandler which deals with the incoming request in its io.undertow.server.HttpHandler#handleRequest method. This handler is registered by io.undertow.servlet.api.DeploymentInfo#addSecurityWrapper with a DeploymentInfo which is provided by an implementation of io.undertow.servlet.ServletExtension. The ServletExtension is registered as a service provider in META-INF/services/io.undertow.servlet.ServletExtension.

    The request handling in our implementation of io.undertow.server.HttpHandler#handleRequest extracts the JWT token from the cookie, pre-validates it and determines the contained username. This username and the token as a password are used as inputs for a call to javax.servlet.http.HttpServletRequest#login.

    With the legacy security subsystem, the behavior of the server was, that this call to login triggered the authentication against the configured legacy security domain AND created a session in Undertow so that the HTTP 200 response for the previous GET request contained a Set-Cookie header with a fresh JSESSIONID cookie.

    With Elytron, javax.servlet.http.HttpServletRequest#login doesn't do anything, neither an authentication against an Elytron security domain and security realm nor the creation of a session is triggered. The browser simply shows the login form which should get skipped by the described interception process.

    I debugged the implementation of javax.servlet.http.HttpServletRequest#login that comes with JBoss. We start in io.undertow.servlet.spec.HttpServletRequestImpl#login which calls login = sc.login(username, password). This SecurityContext, when using Elytron, is org.wildfly.elytron.web.undertow.server.SecurityContextImpl. org.wildfly.elytron.web.undertow.server.SecurityContextImpl#login first checks if (httpAuthenticator == null). The httpAuthenticator is only set in org.wildfly.elytron.web.undertow.server.SecurityContextImpl#authenticate which gets called by a call to javax.servlet.http.HttpServletRequest#authenticate.

    This explains, why a plain call to io.undertow.servlet.spec.HttpServletRequestImpl#login was doing nothing. I tried to call javax.servlet.http.HttpServletRequest#authenticate first, to instantiate that httpAuthenticator internally, and then javax.servlet.http.HttpServletRequest#login. This at least finally triggered the authentication and authorization against the configured Elytron security domain and security realm. Authentication/authorization were successful but Undertow still didn't issue a new JSESSIONID cookie and the browser again showed the login form instead of proceeding to the protected resources.

    I'm currently out of ideas, how to proceed with this issue und how to achieve the same behavior as with the legacy security subsystem. Why does the Elytron implementation of io.undertow.security.api.SecurityContext behave so differently compared to the one for legacy security (io.undertow.security.impl.SecurityContextImpl)? How am I supposed to log in programatically in a FORM based web application using Elytron with javax.servlet.http.HttpServletRequest#login and/or javax.servlet.http.HttpServletRequest#authenticate?

    The relevant JBoss configuration for all this looks like this:


    <application-security-domain name="my_app_security_domain" http-authentication-factory="MyHttpAuthFactory"/>


    <security-domain name="MySecurityDomain" default-realm="MyCachingRealm" permission-mapper="default-permission-mapper">
    <realm name="MyCachingRealm" role-decoder="FromRolesAttributeDecoder"/>

    <custom-realm name="MyCustomRealm" module="module name redacted" class-name="class name redacted"/>
    <caching-realm name="MyCachingRealm" realm="MyCustomRealm" maximum-age="300000"/>
    <identity-realm name="local" identity="$local"/>

    <simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
    <principal name="anonymous"/>
    <permission-set name="default-permissions"/>
    <permission-mapping match-all="true">
    <permission-set name="login-permission"/>
    <permission-set name="default-permissions"/>
    <constant-realm-mapper name="local" realm-name="local"/>
    <constant-realm-mapper name="MyRealmMapper" realm-name="MyCachingRealm"/>
    <simple-role-decoder name="FromRolesAttributeDecoder" attribute="Roles"/>

    <http-authentication-factory name="MyHttpAuthFactory" security-domain="MySecurityDomain" http-server-mechanism-factory="global">
    <mechanism mechanism-name="FORM" realm-mapper="MyRealmMapper">
    <mechanism-realm realm-name="MyRealm"/>
    <provider-http-server-mechanism-factory name="global"/>

    Continue reading...

Compartilhe esta Página