I'm fighting with an Azure AD B2C setup.
I want a few public paths, and everything else to require an authenticated JWT to access. I'm just trying to get it all to work locally with a simple case. so no scopes yet.
I'm using an Authorization Code Flow.
My response is interesting - but I don't quite understand it. It indicates my token is not ready? (the 'nbf' value issued is about 4minutes in the future.)
Any thoughts, tips or insights would be very appreciated!!
Is there someway to sync the clock on Azure AD B2c?
Response:
{
...
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Jwt used before 2021-08-31T10:51:59Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
...
}
Bearer token provides nbf with an epoch @ Tuesday, August 31, 2021 6:51:59 AM GMT-04:00 DST:
{
"iss": "https://<aad b2c dir name>.b2clogin.com/tfp/*<uuid: azure ad directory id>*/b2c_1_signin/v2.0/",
"exp": 1630410719,
"nbf": 1630407119,
"aud": "<uuid: Spring App client id>",
"oid": "00af8715-0d5b-471b-bd2a-bf24c301c4eb",
"sub": "00af8715-0d5b-471b-bd2a-bf24c301c4eb",
"name": "...",
"given_name": "...",
"family_name": "...",
"tfp": "B2C_1_signin",
"scp": "Organizaiton.Read",
"azp": "<uuid: Spring App client id>",
"ver": "1.0",
"iat": 1630407119
}
Application Log is 3 minutes behind: (local machine - I have synced the time with the time servers)
2021-08-31 06:48:59.786 DEBUG 19258 --- [nio-8080-exec-2] o.a.coyote.http11.Http11InputBuffer : Before fill(): parsingHeader: [true], parsingRequestLine: [true], parsingRequestLinePhase: [0], parsingRequestLineStart: [0], byteBuffer.position(): [0], byteBuffer.limit(): [0], end: [0]
2021-08-31 06:48:59.789 DEBUG 19258 --- [nio-8080-exec-2] o.a.tomcat.util.net.SocketWrapperBase : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@37f715bf:org.apache.tomcat.util.net.SecureNioChannel@72f58d25:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:35298]], Read from buffer: [0]
2021-08-31 06:48:59.790 DEBUG 19258 --- [nio-8080-exec-2] org.apache.tomcat.util.net.NioEndpoint : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@37f715bf:org.apache.tomcat.util.net.SecureNioChannel@72f58d25:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:35298]], Read direct from socket: [1510]
2021-08-31 06:48:59.790 DEBUG 19258 --- [nio-8080-exec-2] o.a.coyote.http11.Http11InputBuffer : Received [GET /org/85aaa62b-5cbd-486d-b0a2-51e63a9772d7/study/getMembersByStudyId?studyId=03ad2704-728b-40b2-a24e-787c655b7b46 HTTP/1.1
SdkVersion: postman-graph/v2.0
Authorization: Bearer <...>
User-Agent: PostmanRuntime/7.28.3
Accept: */*
Cache-Control: no-cache
Postman-Token: 692e411b-91d7-48a4-a1c4-7b79ac58e5f3
Host: localhost:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: JSESSIONID=D238CDCAF4E81EEE3C26F5F55B8B930A
]
2021-08-31 06:48:59.798 DEBUG 19258 --- [nio-8080-exec-2] o.a.t.util.http.Rfc6265CookieProcessor : Cookies: Parsing b[]: JSESSIONID=D238CDCAF4E81EEE3C26F5F55B8B930A
2021-08-31 06:48:59.800 DEBUG 19258 --- [nio-8080-exec-2] o.a.catalina.connector.CoyoteAdapter : Requested cookie session id is D238CDCAF4E81EEE3C26F5F55B8B930A
2021-08-31 06:48:59.803 DEBUG 19258 --- [nio-8080-exec-2] o.a.c.authenticator.AuthenticatorBase : Security checking request GET /org/85aaa62b-5cbd-486d-b0a2-51e63a9772d7/study/getMembersByStudyId
2021-08-31 06:48:59.804 DEBUG 19258 --- [nio-8080-exec-2] org.apache.catalina.realm.RealmBase : No applicable constraints defined
2021-08-31 06:48:59.819 DEBUG 19258 --- [nio-8080-exec-2] o.a.c.a.jaspic.AuthConfigFactoryImpl : Loading persistent provider registrations from [/tmp/tomcat.8080.14472900442435584992/conf/jaspic-providers.xml]
2021-08-31 06:48:59.819 DEBUG 19258 --- [nio-8080-exec-2] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
2021-08-31 06:48:59.822 INFO 19258 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-08-31 06:48:59.823 INFO 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-08-31 06:48:59.823 TRACE 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@40a9f818
2021-08-31 06:48:59.825 TRACE 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@2d0d23ac
2021-08-31 06:48:59.826 TRACE 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver@2c06ef36
2021-08-31 06:48:59.827 TRACE 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
2021-08-31 06:48:59.828 TRACE 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
2021-08-31 06:48:59.828 DEBUG 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2021-08-31 06:48:59.828 INFO 19258 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2021-08-31 06:48:59.833 DEBUG 19258 --- [nio-8080-exec-2] o.apache.catalina.core.StandardWrapper : Returning non-STM instance
2021-08-31 06:48:59.869 TRACE 19258 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.sciteline.controllers.StudyController#getMembersByStudyId(UUID, UUID)
2021-08-31 06:48:59.871 DEBUG 19258 --- [nio-8080-exec-2] org.apache.tomcat.util.http.Parameters : Set encoding to UTF-8
2021-08-31 06:48:59.872 DEBUG 19258 --- [nio-8080-exec-2] org.apache.tomcat.util.http.Parameters : Decoding query null UTF-8
2021-08-31 06:48:59.872 DEBUG 19258 --- [nio-8080-exec-2] org.apache.tomcat.util.http.Parameters : Start processing with input [studyId=03ad2704-728b-40b2-a24e-787c655b7b46]
2021-08-31 06:48:59.930 DEBUG 19258 --- [nio-8080-exec-2] o.s.web.client.RestTemplate : HTTP GET https://scitelineapps.b2clogin.com/scitelineapps.onmicrosoft.com/b2c_1_signin/discovery/v2.0/keys
2021-08-31 06:48:59.934 DEBUG 19258 --- [nio-8080-exec-2] o.s.web.client.RestTemplate : Accept=[text/plain, application/json, application/*+json, */*]
2021-08-31 06:49:00.660 DEBUG 19258 --- [nio-8080-exec-2] o.s.web.client.RestTemplate : Response 200 OK
2021-08-31 06:49:00.663 DEBUG 19258 --- [nio-8080-exec-2] o.s.web.client.RestTemplate : Reading to [java.lang.String] as "application/json;charset=utf-8"
2021-08-31 06:49:00.701 DEBUG 19258 --- [nio-8080-exec-2] o.a.coyote.http11.Http11InputBuffer : Before fill(): parsingHeader: [true], parsingRequestLine: [true], parsingRequestLinePhase: [0], parsingRequestLineStart: [0], byteBuffer.position(): [0], byteBuffer.limit(): [0], end: [1510]
2021-08-31 06:49:00.702 DEBUG 19258 --- [nio-8080-exec-2] o.a.tomcat.util.net.SocketWrapperBase : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@37f715bf:org.apache.tomcat.util.net.SecureNioChannel@72f58d25:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:35298]], Read from buffer: [0]
2021-08-31 06:49:00.702 DEBUG 19258 --- [nio-8080-exec-2] org.apache.tomcat.util.net.NioEndpoint : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@37f715bf:org.apache.tomcat.util.net.SecureNioChannel@72f58d25:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:35298]], Read direct from socket: [0]
2021-08-31 06:49:00.702 DEBUG 19258 --- [nio-8080-exec-2] o.a.coyote.http11.Http11InputBuffer : Received []
Just my configuration, in case you see something odd, or have suggestions:
Here's the WebSecurityConfig:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(10)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// CORs on, CSRF off (stateless sessions instead)
http.cors().and().csrf().disable();
// Set session management to stateless
http = http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and();
// Set unauthorized requests exception handler
http = http.exceptionHandling().authenticationEntryPoint((request, response, ex) ->
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
}).and();
// Set permissions on endpoints
http.authorizeRequests()
// public endpoints
.antMatchers(HttpMethod.POST, "/addParticipant").permitAll()
.antMatchers("/browseStudies").permitAll()
.antMatchers(HttpMethod.POST, "/submitsurvey").permitAll();
http.
// private endpoints
.authorizeRequests(authz -> authz
.anyRequest().hasAuthority("SCOPE_Organizaiton.Read"))
// .anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt()
.jwtAuthenticationConverter(new
AADB2CJwtBearerTokenAuthenticationConverter()));
}
}
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/>
</parent>
...
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot-starter-active-directory-b2c</artifactId>
<version>3.7.0</version>
</dependency>
application.properties:
...
azure.activedirectory.client-id=<client_id>
azure.activedirectory.app-id-uri=api://<client_id>
spring.security.oauth2.resourceserver.jwt.jwk-set-uri:https://<az ad b2c directory>.b2clogin.com/<azure ad b2c tenant>/b2c_1_signin/discovery/v2.0/keys
spring.security.filter.order=101
server.max-http-header-size=20KB
...
For Posterity, the root cause here was not configuration related, rather, the time of the locally running App Server was running behind.
The Application server in WSL and the WSL OS time was running behind system clock and AD server.
The WSL issue has been patched in later versions of WSL.
If you haven't updated WSL, you can reset the clock manually: sudo hwclock -s