Closed jsm174 closed 5 years ago
I coded things in JHipster 6 so a resource server is available automatically when using OAuth 2 for Auth. I’ve been testing it this week with an Ionic client and it seems to work just fine. I’ve only tested a monolith though, I haven’t tested it with a gateway. I’ll try to do that today.
On Jun 12, 2019, at 07:15, Jason Millard notifications@github.com wrote:
Overview of the issue
In JHipster 5.8.2 we were using a Gateway as a resource server by using @ruddell 's ResourceServerConfiguration.java found here.
We had a custom react website (with a different domain) that manages oauth2 tokens from keycloak using keycloak.js
We would then add the authorization header using those tokens while calling the microservice using axios.
In JHipster 6.x.x, oauth2 support has changed significantly. According to #9276, @EnableResourceServer is no longer recommended.
With a stock Gateway, when attempting to access the microservice, Chrome will do a CORS preflight call using OPTIONS. Because the preflight does not send the Authorization header, AuthorizationHeaderUtil.java will crash when trying to determine the OAuth2AuthorizedClient (oauthToken is null):
public Optional
getAuthorizationHeader() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication; OAuth2AuthorizedClient client = clientService.loadAuthorizedClient( oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
I tried modifying getAuthorizationHeader() to just return Optional.empty() but then I start receiving 401's which I think is because of AnonymousAuthenticationToken:
o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9c243cda: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffa64e: RemoteIpAddress: 192.168.1.237; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' Motivation for or Use Case
Would really like to update our app to latest JHipster prior to going live.
Reproduce the error
Related issues
Suggest a Fix
JHipster Version(s)
gateway@0.0.0 /Users/jmillard/jhipster/latest3/gateway └── generator-jhipster@6.1.0
Environment and Tools
openjdk version "12.0.1" 2019-04-16 OpenJDK Runtime Environment AdoptOpenJDK (build 12.0.1+12) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 12.0.1+12, mixed mode, sharing)
git version 2.20.1 (Apple Git-117)
node: v10.15.3
npm: 6.4.1
Docker version 18.09.2, build 6247962
docker-compose version 1.23.2, build 1110ad01
Browsers and Operating System
Checking this box is mandatory (this is just to show you read everything) — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
This scenario is currently broken on Gateway due to unconditional typecast to OAuth2AuthenticationToken
, which wouldn't hold true in authorization server scenario (token type would be JwtAuthenticationToken
). It's fix is also included in #9874
@vishal423 - I agree about the unconditional typecast, but in my use case, it's not even a JwtAuthenticationToken.
I temporarily disabled web security in chrome, to see if I could get avoid the OPTIONS preflight call:
open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Sure enough, I now get the JwtAuthenticationToken
for SecurityContextHolder.getContext().getAuthentication();
So I will have to wait for https://github.com/jhipster/generator-jhipster/pull/9874
As for the CORS issue, I tried to step through CorsFilter.java
. corsConfiguration
is null so it just moves on to the next filter.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
if (corsConfiguration != null) {
boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
return;
}
}
}
filterChain.doFilter(request, response);
}
Okay, I think I figured out the CORS part of this issue.
PR https://github.com/jhipster/generator-jhipster/pull/9262 updated the microservice path prefix to begin with /services/
In the Gateway's WebConfigurer.java
, I added the following:
source.registerCorsConfiguration("/services/*/api/**", config);
to:
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
source.registerCorsConfiguration("/*/api/**", config);
source.registerCorsConfiguration("/services/*/api/**", config);
source.registerCorsConfiguration("/*/management/**", config);
}
return new CorsFilter(source);
}
That at least gets me to getAuthorizationHeader()
with a JwtAuthenticationToken
!
@jsm174 I just tried creating a Gateway with 6.1.0 and using an Ionic client to communicate with it. It all works fine and I didn't have any CORS issues. I'm using OAuth 2.0 for auth. Here's the output of jhipster info
in my gateway project.
oauthgateway@0.0.0 /Users/mraible/oauthgateway
└── generator-jhipster@6.1.0
.yo-rc.json
file generated in the root folder{ "generator-jhipster": { "promptValues": { "packageName": "com.mycompany.myapp", "nativeLanguage": "en" }, "jhipsterVersion": "6.1.0", "applicationType": "gateway", "baseName": "oauthgateway", "packageName": "com.mycompany.myapp", "packageFolder": "com/mycompany/myapp", "serverPort": "8080", "authenticationType": "oauth2", "cacheProvider": "ehcache", "enableHibernateCache": true, "websocket": false, "databaseType": "sql", "devDatabaseType": "h2Disk", "prodDatabaseType": "mysql", "searchEngine": false, "messageBroker": false, "serviceDiscoveryType": "eureka", "buildTool": "maven", "enableSwaggerCodegen": false, "useSass": true, "clientPackageManager": "npm", "clientFramework": "angularX", "clientTheme": "none", "clientThemeVariant": "", "testFrameworks": ["protractor"], "jhiPrefix": "jhi", "entitySuffix": "", "dtoSuffix": "DTO", "otherModules": [], "enableTranslation": true, "nativeLanguage": "en", "languages": ["en"] } }
entityName.json
files generated in the .jhipster
directoryentity Blog { name String required minlength(3), handle String required minlength(2) } entity Entry { title String required, content TextBlob required, date Instant required } entity Tag { name String required minlength(2) } relationship ManyToOne { Blog{user(login)} to User, Entry{blog(name)} to Blog } relationship ManyToMany { Entry{tag(name)} to Tag{entry} } paginate Entry, Tag with infinite-scroll
openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
git version 2.20.1 (Apple Git-117)
node: v10.15.3
npm: 6.9.0
yeoman: 2.0.6
Docker version 18.09.2, build 6247962
docker-compose version 1.23.2, build 1110ad01
@mraible Thank you for testing. Was your ionic app hitting a microservice that is proxied via the gateway?
After stepping through this, I'm positive corsFilter
needs to be updated to account for the microservice path prefix.
I only generated the entities on my gateway. I did not create any microservices. Let me try that.
@jsm174 After testing everything with microservices, it seems there's some additional logic that's needed to get the access token (and user information) from a JwtAuthenticationToken
. https://github.com/jhipster/generator-jhipster/pull/9905 does that. I did not need to make any CORS changes when testing this.
Thanks. Thats great!
Is your gateway's application.yml
zuul.prefix
set to /services
?
zuul: # those values must be configured depending on the application specific needs
sensitive-headers: Cookie,Set-Cookie #see https://github.com/spring-cloud/spring-cloud-netflix/issues/3126
host:
max-total-connections: 1000
max-per-route-connections: 100
prefix: /services
semaphore:
max-semaphores: 500
When I call the microservice directly, I'm using axios and hitting
http://gateway:8080/services/testmicroservice/api/sampleservice
@jsm174 Yes, my zuul.prefix
is set to /services
. I did not change anything in application.yml
:
zuul: # those values must be configured depending on the application specific needs
sensitive-headers: Cookie,Set-Cookie #see https://github.com/spring-cloud/spring-cloud-netflix/issues/3126
host:
max-total-connections: 1000
max-per-route-connections: 100
prefix: /services
semaphore:
max-semaphores: 500
While we're on the subject, having different API endpoints for a monolith vs a microservices architecture seems like a bad design to me. Isn't the whole point of an API gateway to figure out the URLs for you?
It seems odd to me that my Ionic client has to worry about if it should call http://localhost:8080/api/blogs
(for a monolith) or http://localhost:8080/services/blog/api/blogs
(for microservices). IMO, it should be possible to call /api/blogs
and let the backend figure out where it gets its data from.
WDYT @jhipster/developers?
@mraible I really appreciate you taking the time to look into this.
I am at a loss why CORS is not an issue for you and it is for me.
All I can say is my gateway and webapp (that accesses the microservices) have different domains. ie http://localhost:8080
vs http://web.mydomain.com:3000
and PR https://github.com/jhipster/generator-jhipster/pull/9906 fixes it.
TBH, I don't know much about ionic. If it's just a web browser wrapper, I can't see it working different than Chrome. It would have to be executing the CORS preflights.
Ionic 4 uses Angular and I’m running it in Chrome so it’s not that different from JHipster’s UI. It runs on localhost:8100 and JHipster runs on 8080. Different ports works the same as different domains AFAIK. Are you using JWT for Auth? I’m using OIDC.
On Jun 12, 2019, at 18:06, Jason Millard notifications@github.com wrote:
@mraible I really appreciate you taking the time to look into this.
I am at a loss why CORS is not an issue for you and it is for me.
All I can say is my gateway and webapp (that accesses the microservices) have different domains. ie http://localhost:8080 vs http://web.mydomain.com:3000 and PR #9906 fixes it.
TBH, I don't know much about ionic. If it's just a web browser wrapper, I can't see it working different than Chrome. It would have to be executing the CORS preflights.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Different ports works the same as different domains AFAIK.
Agreed!
Are you using JWT for Auth? I’m using OIDC.
Our react website connects to a Keycloak server using keycloak-js. So I'm assuming it is uses oauth2 access tokens.
I'll try to put a sample together.
@jsm174 I closed this issue because it's resolved for me. If you're using keycloak-js, that's not something we currently support in JHipster. I'm not saying I'm against supporting it, but we love to be IdP agnostic. We test against Keycloak and Okta currently, but we should strive to work with all OIDC providers.
@mraible Isn't keycloak-js from Keycloak?
Yes, but we don’t currently use it in JHipster. Everything OAuth related is handled by Spring Security. There is no client side code, except in the mobile app modules (for React Native and Ionic), which both use AppAuth.
On Jun 13, 2019, at 06:24, Jason Millard notifications@github.com wrote:
@mraible Isn't keycloak-js from Keycloak?
https://www.npmjs.com/package/keycloak-js
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@mraible understood. I will look into using AppAuth.
I did put an example project to recreate this issue at:
https://github.com/jsm174/jh610-cors-issue-demo
You'll see the keycloak use is pretty simplistic:
https://github.com/jsm174/jh610-cors-issue-demo/blob/master/frontend/src/App.js
hi im using this sample https://github.com/amrutprabhu/keycloak-spring-cloud-gateway-and-resource-server.git i changed it to connect spring authorization server which is federated with azure b2c when i hit the sign in button it will navigate to azure b2c login page and after that the login of jhipster show my user name and every thing is fine.. i can call any api inside gateway but when i call another resource server like /product which the corresponding route is defined in properties i receive unauthorized from my resource server. i check and found that access token is not present in resource server.. configuration about token relay is present. im using authorization_code grant and azure b2c give me id_token and access token..but this jhipster project not realying access token to resource server... any help? its two weeks im working on it
Hello @imaxkhan. Please don't comment on closed issues because most people won't see them and you won't get much help. I'd suggest you ask your question on Stack Overflow with the "jhipster" tag.
We currently don't have instructions for integrating with Spring Authorization Server, but it should be possible with our OAuth support.
Overview of the issue
In JHipster 5.8.2 we were using a Gateway as a resource server by using @ruddell 's
ResourceServerConfiguration.java
found here.We had a custom react website (with a different domain) that manages oauth2 tokens from
keycloak
usingkeycloak.js
We would then add the authorization header using those tokens while calling the microservice using axios.
In JHipster 6.x.x, oauth2 support has changed significantly. According to https://github.com/jhipster/generator-jhipster/issues/9276,
@EnableResourceServer
is no longer recommended.With a stock Gateway, when attempting to access the microservice, Chrome will do a CORS preflight call using OPTIONS. Because the preflight does not send the Authorization header,
AuthorizationHeaderUtil.java
will crash when trying to determine theOAuth2AuthorizedClient
(oauthToken
isnull
):I tried modifying
getAuthorizationHeader()
to just returnOptional.empty()
but then I start receiving 401's which I think is because ofAnonymousAuthenticationToken
:Motivation for or Use Case
Would really like to update our app to latest JHipster prior to going live.
Reproduce the error
Related issues
Suggest a Fix
JHipster Version(s)
Environment and Tools
openjdk version "12.0.1" 2019-04-16 OpenJDK Runtime Environment AdoptOpenJDK (build 12.0.1+12) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 12.0.1+12, mixed mode, sharing)
git version 2.20.1 (Apple Git-117)
node: v10.15.3
npm: 6.4.1
Docker version 18.09.2, build 6247962
docker-compose version 1.23.2, build 1110ad01
Browsers and Operating System