Skip to content
This repository has been archived by the owner on Aug 28, 2024. It is now read-only.

Azure Active Directory Stateless implementation #661

Closed
afrancoc2000 opened this issue Apr 30, 2019 · 15 comments
Closed

Azure Active Directory Stateless implementation #661

afrancoc2000 opened this issue Apr 30, 2019 · 15 comments
Assignees
Milestone

Comments

@afrancoc2000
Copy link

Environment

  • Spring boot starter:

    • active directory spring boot starter
  • OS Type: Windows

  • Java version:

    • 1.8

Summary

I'm successfuly authenticating using this example, but The authentication is associated to a JSESSIONID , and I have a microservices aproach and would like it to be stateless. Do you have an example of this kind of authentication? so instead of sending a cookie with the JSESSIONID you could send the JWT? or it needs to be manually handled?

Reproduce steps

This is my app code:

SecurityConfig Class

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET,"/api/healthcheck").permitAll()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and()
                .oauth2Login()
                .userInfoEndpoint()
                .oidcUserService(oidcUserService)
        ;

        http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
                .logoutSuccessUrl("/").deleteCookies("JSESSIONID").invalidateHttpSession(true);
    }
}    

Application.properties:

spring.security.oauth2.client.registration.azure.client-id=XXXXXXXXXXX
spring.security.oauth2.client.registration.azure.client-secret=XXXXXXXXXXX
spring.security.oauth2.client.registration.azure.client-name=Azure
spring.security.oauth2.client.registration.azure.provider=azure-oauth-provider
spring.security.oauth2.client.registration.azure.scope=openid, https://graph.microsoft.com/user.read
spring.security.oauth2.client.registration.azure.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.azure.client-authentication-method=post
spring.security.oauth2.client.registration.azure.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.azure-oauth-provider.authorization-uri=https://login.microsoftonline.com/{my-tenant}/oauth2/authorize
spring.security.oauth2.client.provider.azure-oauth-provider.token-uri=https://login.microsoftonline.com/{my-tenant}/oauth2/token
spring.security.oauth2.client.provider.azure-oauth-provider.user-info-uri=https://login.microsoftonline.com/{my-tenant}/openid/userinfo
spring.security.oauth2.client.provider.azure-oauth-provider.jwk-set-uri=https://login.microsoftonline.com/{my-tenant}/discovery/keys
spring.security.oauth2.client.provider.azure-oauth-provider.user-name-attribute=name

azure.activedirectory.tenant-id=common
azure.activedirectory.active-directory-groups=appUser,appAdmin

A Controller:

@Controller
public class HomeController {

    @PreAuthorize("hasAnyRole('appUser', 'appAdmin')")
    @RequestMapping({"/"})
    public String index() {
        return "index.html";
    }    
}

Expected Results

I would like to get a cookie with the JWT in the front end

Actual Results

I'm getting a cookie with the JSESSIONID, I found these articles with stateless implementations: example 1 and example 2 but both are custom implementations. So I was wondering if the library created for Azure AD had this problem already resolved.

Also something weird happens to me, only GET requests are successfully authenticated POST requests returned a 403 error before I disabled the csrf token and now return a 500 error with a AuthenticationCredentialsNotFoundException: "An Authentication object was not found in the SecurityContext"

Thanks!

@lhanson
Copy link
Contributor

lhanson commented May 13, 2019

It looks like this is similar in nature to #494, and perhaps soon to be addressed by #512.

@afrancoc2000
Copy link
Author

You're right thats exactly what I need thanks

@saragluna saragluna added the aad label Sep 10, 2019
@cybersuv
Copy link

Its almost one year old post here. Is the stateless implementation of Azure AD spring boot starter released as maven repository yet ?

@saragluna
Copy link
Contributor

@cybersuv
Yes, it's been released.

@lhanson
Copy link
Contributor

lhanson commented May 21, 2020

@cybersuv
Yes, it's been released.

When? What version? How would people following this issue ever have known that without noticing how old it is and trying to bump the conversation?

@saragluna
Copy link
Contributor

saragluna commented May 21, 2020

@lhanson It's been released for a while and after this #512 went into master branch. You could try it with latest version, either 2.1.10 with Spring Boot 2.1.x or 2.2.4 with Spring Boot 2.2.x.

@cybersuv
Copy link

@cybersuv
Yes, it's been released.

Good to know this. But, why I am still receiving the below error ?

"failureReason": "The application tried to perform a silent sign in and the user could not be silently signed in. The application needs to start an interactive flow giving users an option to sign in. Contact app owner."

Is there any specific type of app registration needed to support stateless implementation ? My use case is as below.

I have a react front end application which is acquiring token from Azure AD using react-adal library and I want my back-end micro-services (written in Java, spring-boot) to acknowledge/validate the same token passed in Authorization header as Bearer.

Further detail :

Spring Boot Version : 2.2.4
Azure AD Spring Boot Starter : 2.2.4
Configurations :

# App Registration's Application ID:
spring.security.oauth2.client.registration.azure.client-id=XXXXXX-YYYY-ZZZZ-AAAA-TTTTTTTT
# App Registration's secret key:
spring.security.oauth2.client.registration.azure.client-secret=jdkfldsjkfldjsfl;dsjfl;dsjmfijs789ur8w4u5w4;=

spring.security.oauth2.client.registration.azure.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.azure.authorization-grant-type=urn:ietf:params:oauth:grant-type:jwt-bearer
spring.security.oauth2.client.registration.azure.scope=openid

@saragluna
Copy link
Contributor

@cybersuv

In your case both stateful and stateless filters could be used. But the configurations you listed are for OAuth 2 login which is not suitable for your case. If you want to use users' groups (will send requests to Microsoft Graph) to do authorization, please refer to this sample. Otherwise, this is the sample leverages App Role feature of AAD.

@cybersuv
Copy link

cybersuv commented May 28, 2020

@saragluna

Using the second sample link, I updated my code. But, getting following error.

2020-05-28 10:24:50.605 ERROR 19452 --- [nio-8080-exec-1] .AADAppRoleStatelessAuthenticationFilter : Failed to initialize UserPrincipal.

com.nimbusds.jose.RemoteKeySourceException: Couldn't retrieve remote JWK set: Read timed out
	at com.nimbusds.jose.jwk.source.RemoteJWKSet.updateJWKSetFromURL(RemoteJWKSet.java:167) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jose.jwk.source.RemoteJWKSet.get(RemoteJWKSet.java:258) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jose.proc.JWSVerificationKeySelector.selectJWSKeys(JWSVerificationKeySelector.java:113) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.selectKeys(DefaultJWTProcessor.java:288) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:347) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:308) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:299) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.microsoft.azure.spring.autoconfigure.aad.UserPrincipalManager.buildUserPrincipal(UserPrincipalManager.java:126) ~[azure-spring-boot-2.2.4.jar:na]
	at com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter.doFilterInternal(AADAppRoleStatelessAuthenticationFilter.java:58) ~[azure-spring-boot-2.2.4.jar:na]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_202]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_202]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.30.jar:9.0.30]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_202]
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_202]
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:1.8.0_202]
	at java.net.SocketInputStream.read(SocketInputStream.java:171) ~[na:1.8.0_202]
	at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:1.8.0_202]
	at sun.security.ssl.InputRecord.readFully(InputRecord.java:465) ~[na:1.8.0_202]
	at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:593) ~[na:1.8.0_202]
	at sun.security.ssl.InputRecord.read(InputRecord.java:529) ~[na:1.8.0_202]
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975) ~[na:1.8.0_202]
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367) ~[na:1.8.0_202]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395) ~[na:1.8.0_202]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379) ~[na:1.8.0_202]
	at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) ~[na:1.8.0_202]
	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) ~[na:1.8.0_202]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564) ~[na:1.8.0_202]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492) ~[na:1.8.0_202]
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263) ~[na:1.8.0_202]
	at com.nimbusds.jose.util.DefaultResourceRetriever.getInputStream(DefaultResourceRetriever.java:249) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jose.util.DefaultResourceRetriever.retrieveResource(DefaultResourceRetriever.java:201) ~[nimbus-jose-jwt-7.9.jar:7.9]
	at com.nimbusds.jose.jwk.source.RemoteJWKSet.updateJWKSetFromURL(RemoteJWKSet.java:165) ~[nimbus-jose-jwt-7.9.jar:7.9]
	... 55 common frames omitted

@yiliuTo
Copy link
Member

yiliuTo commented Jun 1, 2020

Hi @cybersuv , you can refer to #417 to see whether you issue can be solved.

@sanalsfingent
Copy link

sanalsfingent commented Oct 31, 2022

You're right thats exactly what I need thanks

Hi @afrancoc2000 , Can you please help me understand the solution here? I am in the exact same situation as you were. After login all the requests are passing with JSESSIONID and the token received from OidcUser principal; is not necessary for the application as of now. Also everything except my GET requests are failing unless I specifically disable csrf with http.csrf().disable(); which I believe is not the proper way!

How can I make use of the token and not the JSESSIONID. I am fairly new with Spring Security and is having difficulty finding it on my own.

@yiliuTo yiliuTo added this to the 2022-11 milestone Nov 1, 2022
@chenrujun
Copy link

chenrujun commented Nov 1, 2022

Hi, @sanalsfingent

Thanks for reaching out.

Could you please try this sample (aad-resource-server)?

If the sample cannot satisfy your requirement, could you please create a new issue in azure-sdk-for-java repo?
And it's better to create a demo project to reproduce the problem.

@sm0217
Copy link

sm0217 commented Jan 18, 2023

Hi @chenrujun Need a help here with stateless implementation. The current example for stateless implementation works with oauth2 implicit grant which is no longer recommended according to the docs.

With the current version, Is it possible to use the stateless feature with authorization code flow or any way to work around it? Thanks.

@chenrujun
Copy link

chenrujun commented Jan 19, 2023

HI, @moarychan , As mentioned in the scrum meeting by @saragluna , could you please help about this?

@moarychan
Copy link
Member

@sm0217 , since this issue had been closed for a long time, could you help open a new issue in this repo azure-sdk-for-java to tell us your detailed scenario?

@chenrujun chenrujun assigned moarychan and unassigned chenrujun Jan 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants