JanssenProject / jans

An open source enterprise digital identity platform for CIAM or workforce... Janssen is a distribution of standards-based, developer friendly, components that are engineered to work together in any cloud. #OAuth #OpenID #FIDO
https://docs.jans.io
Apache License 2.0
456 stars 74 forks source link

CORS-related OIDC configuration problem with OwnCloud #7736

Closed tailnet-h-usky-io closed 7 months ago

tailnet-h-usky-io commented 7 months ago

Hello, Janssenists...

I am trying to integrate a fresh installation of Janssen v1.0.22 with [OwnCloud Infinite Scale][1] via OIDC. The hosts are on a private subnet using the domain h.usky.io and dedicated DNS servers. Helm-based setup of both software systems works fine, and even the authentication workflow almost succeeds:

  1. OwnCloud (oc.h.usky.io) redirects to the Janssen (sso.h.usky.io) sign-in page
  2. Authentication Credentials are accepted and the requested OIDC scopes for OwnCloud (profile, mail, etc.) are presented to the user for acceptance
  3. Browser redirects to the OIDC callback page on OwnCloud (https://oc.h.usky.io/oidc-callback)

However, when the landing page on OwnCloud attempts to fetch the authorization token via Javascript (a request to https://[my domain]/jans-auth/restv1/token) , a CORS violation occurs and the browser blocks the request.

https://sso.h.usky.io/jans-auth/restv1/token
Request Method:
POST
Status Code:
401 Unauthorized
Referrer Policy:
strict-origin-when-cross-origin 

It seems that https://sso.h.usky.io/ needs to be added to the CORS configuration for the OwnCloud frontend. I believe I have done this correctly, but it's still not working.

I'm also [told][2] that I also need to do the opposite: That is, https://oc.h.usky.io needs to be added to Janssen's CORS configuration for Janssen. Again, I believe I have done this correctly, but it's still not working.

I'm deploying Janssen (and OwnCloud) using the Helm charts, and the CORS-related configuration values do seem to be making it into the persistence layer. I'm using LDAP...

❯ ldapsearch -D 'cn=Directory Manager' -H ldaps://ldap.h.usky.io:1636 -w $LDAPPWD \
  -b 'ou=jans-auth,ou=configuration,o=jans'  jansConfDyn | grep -C 4 'oc.h.usky.io'

 baseDn":"ou=people,o=jans"},{"filter":"uid={0}","bind":true,"bindPasswordAttr
 ibute":"pwd","baseDn":"ou=people,o=jans"}],"clientAuthenticationFilters":[{"f
 ilter":"myCustomAttr1={0}","bind":null,"bindPasswordAttribute":"","baseDn":"o
 u=clients,o=jans"}],"corsConfigurationFilters":[{"filterName":"CorsFilter","c
 orsEnabled":true,"corsAllowedOrigins":"https://oc.h.usky.io","corsAllowedMeth
 ods":"GET,POST,HEAD,OPTIONS","corsAllowedHeaders":"Origin,Authorization,Accep
 t,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-
 Request-Headers","corsExposedHeaders":"","corsSupportCredentials":true,"corsL
 oggingEnabled":true,"corsPreflightMaxAge":1800,"corsRequestDecorate":true}],"

The relevant embedded JSON is:

    "corsConfigurationFilters": [
        {
            "filterName": "CorsFilter",
            "corsEnabled": true,
            "corsAllowedOrigins": "https://oc.h.usky.io",
            "corsAllowedMethods": "GET,POST,HEAD,OPTIONS",
            "corsAllowedHeaders": "Origin,Authorization,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers",
            "corsExposedHeaders": "",
            "corsSupportCredentials": true,
            "corsLoggingEnabled": true,
            "corsPreflightMaxAge": 1800,
            "corsRequestDecorate": true
        }
    ],

So the config values have made it at least as far the LDAP database, but I'm not sure how to check whether they're present in the containers. It seems they're definitely not all the way to the browser, so it feels like I'm missing something obvious.

Any help or tips debugging this will be greatly appreciated. The auth server log (in DEBUG) is shown below, but does not show the auth token request (since it was bloacked by CORS), only the previous one.

Thanks in advance, -b


The Janssen server is hosted at: https://sso.h.usky.io OwnCloud is hosted at: https://oc.h.usky.io

Steps to reproduce

  1. Establish OIDC provider at https://sso.[some domain]/
  2. Deploy Janssen from the Helm charts to https://sso.[some domain], and set the corsConfigurationFilters as per above:
    "corsConfigurationFilters": [
        {
            "filterName": "CorsFilter",
            "corsEnabled": true,
            "corsAllowedOrigins": "https://oc.[some domain]",
            "corsAllowedMethods": "GET,POST,HEAD,OPTIONS",
            "corsAllowedHeaders": "Origin,Authorization,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers",
            "corsExposedHeaders": "",
            "corsSupportCredentials": true,
            "corsLoggingEnabled": true,
            "corsPreflightMaxAge": 1800,
            "corsRequestDecorate": true
        }
    ]
  3. Deploy OCIS from the Helm charts to https://oc.[some domain], using the following overrides:
    
    externalDomain: oc.[some domain]

HTTP settings for oCIS services.

http:

-- CORS settings for oCIS services.

cors:

-- allow_origins is a list of origins a cross-domain request can be executed from.

# If the special "*" value is present in the list, all origins will be allowed.
allow_origins: ["*", "https://sso.[some domain]", "https://oc.[some domain]", "https://[some domain]"]

... features: basicAuthentication: false externalUserManagement: enabled: true

3. Attempt to login

### Expected behaviour
Browser Javascript requests to `https://sso.[some domain]` should be allowed from OwnCloud

### Screenshots ###

Here's the _(successful)_ redirect to the OwnCloud callback URL once scopes have been granted, just before the auth token request:
<img width="1243" alt="oidc-callback" src="https://github.com/JanssenProject/jans/assets/157428625/b6177d21-7d02-45d1-9ae1-4b811a689c5a">

..and the result of the CORS-blocked auth token request:
<img width="1214" alt="token-request" src="https://github.com/JanssenProject/jans/assets/157428625/a2c479d0-148c-4f00-b35f-118b7b2d3191">

### Actual behaviour
Browser Javascript requests to `https://sso.[some domain]` are blocked by CORS configuration. :( 

### Server configuration
**Operating system**: Ubuntu 22.04 LTS + microk8s

**Web server:** Whatever is packaged into Janssen edition `v1.0.22` containers

**Database:** Whatever is packaged into Janssen edition `v1.0.22` containers

### Client configuration
**Browser:** Chrome, Firefox, Safari _(all tested - same results)_

**Operating system:** Windows, Mac, Linux _(all tested - same results)_

[1]:https://owncloud.com/infinite-scale-4-0/
[2]:https://github.com/owncloud/ocis/issues/8393#issuecomment-1943107953

### Auth server log ###

auth - 2024-02-15 02:58:50,144 DEBUG [qtp1492875057-41] [io.jans.service.cache.InMemoryCacheProvider] (InMemoryCacheProvider.java:46) - Starting InMemoryCacheProvider ... auth - 2024-02-15 02:58:50,145 DEBUG [qtp1492875057-41] [io.jans.service.cache.InMemoryCacheProvider] (InMemoryCacheProvider.java:50) - InMemoryCacheProvider started. auth - 2024-02-15 02:58:50,260 DEBUG [qtp1492875057-31] [as.server.authorize.ws.rs.AuthorizeRestWebServiceImpl] (AuthorizeRestWebServiceImpl.java:276) - Attempting to request authorization: AuthzRequest{scope='openid profile email', responseType='code', clientId='26c7c733-6839-47d6-831a-eec2244b6eaf', redirectUri='https://oc.h.usky.io/oidc-callback.html', state='57a9c75e9f5b4d55a1c4e88c6b79c717', responseMode='query', nonce='null', display='null', prompt='null', maxAge=null, uiLocales='null', idTokenHint='null', loginHint='null', acrValues='null', deviceSession='null', amrValues='null', request='null', requestUri='null', authzDetailsString='null', authzDetails='null', sessionId='null', originHeaders='null', codeChallenge='1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg', codeChallengeMethod='S256', customResponseHeaders='null', customParameters='null', claims='null', authReqId='null', httpRequest=Request(GET https://sso.h.usky.io:443/jans-auth/restv1/authorize?client_id=26c7c733-6839-47d6-831a-eec2244b6eaf&redirect_uri=https%3A%2F%2Foc.h.usky.io%2Foidc-callback.html&response_type=code&scope=openid+profile+email&state=57a9c75e9f5b4d55a1c4e88c6b79c717&code_challenge=1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg&code_challenge_method=S256&response_mode=query)@254a7805, httpResponse=HTTP/1.1 200 , securityContext=org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext@545cda1} auth - 2024-02-15 02:58:50,278 DEBUG [qtp1492875057-31] [io.jans.as.server.service.RedirectionUriService] (RedirectionUriService.java:162) - Comparing https://oc.h.usky.io/oidc-callback.html == https://oc.h.usky.io/oidc-callback.html auth - 2024-02-15 02:58:50,278 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:58) - Checking scopes policy for: [openid, profile, email] auth - 2024-02-15 02:58:50,286 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:91) - Granted scopes: [openid, profile, email] auth - 2024-02-15 02:58:50,289 DEBUG [qtp1492875057-31] [as.server.authorize.ws.rs.AuthorizeRestWebServiceImpl] (AuthorizeRestWebServiceImpl.java:796) - Redirect to authorization page, request AuthzRequest{scope='openid profile email', responseType='code', clientId='26c7c733-6839-47d6-831a-eec2244b6eaf', redirectUri='https://oc.h.usky.io/oidc-callback.html', state='57a9c75e9f5b4d55a1c4e88c6b79c717', responseMode='query', nonce='null', display='null', prompt='null', maxAge=null, uiLocales='null', idTokenHint='null', loginHint='null', acrValues='null', deviceSession='null', amrValues='null', request='null', requestUri='null', authzDetailsString='null', authzDetails='null', sessionId='null', originHeaders='null', codeChallenge='1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg', codeChallengeMethod='S256', customResponseHeaders='null', customParameters='{}', claims='null', authReqId='null', httpRequest=Request(GET https://sso.h.usky.io:443/jans-auth/restv1/authorize?client_id=26c7c733-6839-47d6-831a-eec2244b6eaf&redirect_uri=https%3A%2F%2Foc.h.usky.io%2Foidc-callback.html&response_type=code&scope=openid+profile+email&state=57a9c75e9f5b4d55a1c4e88c6b79c717&code_challenge=1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg&code_challenge_method=S256&response_mode=query)@254a7805, httpResponse=HTTP/1.1 200 , securityContext=org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext@545cda1} auth - 2024-02-15 02:58:50,289 DEBUG [qtp1492875057-31] [as.server.authorize.ws.rs.AuthorizeRestWebServiceImpl] (AuthorizeRestWebServiceImpl.java:935) - Redirecting to https://sso.h.usky.io/jans-auth/authorize.htm auth - 2024-02-15 02:58:50,432 DEBUG [qtp1492875057-41] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:58) - Checking scopes policy for: [openid, profile, email] auth - 2024-02-15 02:58:50,433 DEBUG [qtp1492875057-41] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:91) - Granted scopes: [openid, profile, email] auth - 2024-02-15 02:58:50,433 DEBUG [qtp1492875057-41] [io.jans.as.server.service.RedirectionUriService] (RedirectionUriService.java:162) - Comparing https://oc.h.usky.io/oidc-callback.html == https://oc.h.usky.io/oidc-callback.html auth - 2024-02-15 02:58:55,327 DEBUG [qtp1492875057-31] [io.jans.as.server.service.AuthenticationService] (AuthenticationService.java:128) - Authenticating user with LDAP: username: 'testuser', credentials: '1044464744' auth - 2024-02-15 02:58:55,337 DEBUG [qtp1492875057-31] [jans.as.common.service.common.UserService] (UserService.java:78) - Getting user information from LDAP: userId = testuser auth - 2024-02-15 02:58:55,341 DEBUG [qtp1492875057-31] [jans.as.common.service.common.UserService] (UserService.java:93) - Found 1 entries for user id = testuser auth - 2024-02-15 02:58:55,356 DEBUG [qtp1492875057-31] [io.jans.as.server.service.SessionIdService] (SessionIdService.java:528) - Changing session id from d36d6721-f60f-417e-b7c1-5b41ba2f021b to 2b7bab77-4fb9-4acd-ba6b-5cadd9a2c70e ... auth - 2024-02-15 02:58:55,361 DEBUG [qtp1492875057-31] [io.jans.as.server.service.SessionIdService] (SessionIdService.java:537) - Session identifier changed from d36d6721-f60f-417e-b7c1-5b41ba2f021b to 2b7bab77-4fb9-4acd-ba6b-5cadd9a2c70e . auth - 2024-02-15 02:58:55,361 DEBUG [qtp1492875057-31] [io.jans.as.server.auth.Authenticator] (Authenticator.java:466) - Sending event to trigger user redirection: 'testuser' auth - 2024-02-15 02:58:55,362 INFO [qtp1492875057-31] [io.jans.as.server.service.AuthenticationService] (AuthenticationService.java:751) - Attempting to redirect user: SessionUser: 2b7bab77-4fb9-4acd-ba6b-5cadd9a2c70e auth - 2024-02-15 02:58:55,362 INFO [qtp1492875057-31] [io.jans.as.server.service.AuthenticationService] (AuthenticationService.java:759) - Attempting to redirect user: User: BaseEntry [dn=inum=16301a10-7903-4a5c-9445-d32c6372dcbd,ou=people,o=jans] auth - 2024-02-15 02:58:55,363 INFO [qtp1492875057-31] [io.jans.as.server.auth.Authenticator] (Authenticator.java:473) - Authentication success for User: 'testuser' auth - 2024-02-15 02:58:55,363 DEBUG [qtp1492875057-31] [io.jans.as.server.auth.Authenticator] (Authenticator.java:140) - authenticate resultCode: success auth - 2024-02-15 02:58:55,375 DEBUG [qtp1492875057-37] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:58) - Checking scopes policy for: [openid, profile, email] auth - 2024-02-15 02:58:55,376 DEBUG [qtp1492875057-37] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:91) - Granted scopes: [openid, profile, email] auth - 2024-02-15 02:58:55,381 DEBUG [qtp1492875057-37] [io.jans.as.server.service.RedirectionUriService] (RedirectionUriService.java:162) - Comparing https://oc.h.usky.io/oidc-callback.html == https://oc.h.usky.io/oidc-callback.html auth - 2024-02-15 02:58:55,381 DEBUG [qtp1492875057-37] [io.jans.as.server.service.RedirectionUriService] (RedirectionUriService.java:162) - Comparing https://oc.h.usky.io/oidc-callback.html == https://oc.h.usky.io/oidc-callback.html auth - 2024-02-15 02:59:01,359 DEBUG [qtp1492875057-31] [as.server.authorize.ws.rs.AuthorizeRestWebServiceImpl] (AuthorizeRestWebServiceImpl.java:276) - Attempting to request authorization: AuthzRequest{scope='openid profile email', responseType='code', clientId='26c7c733-6839-47d6-831a-eec2244b6eaf', redirectUri='https://oc.h.usky.io/oidc-callback.html', state='57a9c75e9f5b4d55a1c4e88c6b79c717', responseMode='query', nonce='null', display='null', prompt='null', maxAge=null, uiLocales='null', idTokenHint='null', loginHint='null', acrValues='null', deviceSession='null', amrValues='null', request='null', requestUri='null', authzDetailsString='null', authzDetails='null', sessionId='null', originHeaders='null', codeChallenge='1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg', codeChallengeMethod='S256', customResponseHeaders='null', customParameters='null', claims='null', authReqId='null', httpRequest=Request(GET https://sso.h.usky.io:443/jans-auth/restv1/authorize?scope=openid+profile+email&response_type=code&redirect_uri=https%3A%2F%2Foc.h.usky.io%2Foidc-callback.html&state=57a9c75e9f5b4d55a1c4e88c6b79c717&code_challenge_method=S256&client_id=26c7c733-6839-47d6-831a-eec2244b6eaf&code_challenge=1sEX4mPwWSub_ZK30ZqWlLqYSKWfKraXHJlpd0U7avg&response_mode=query&sid=d6d9fe1c-c5e8-4208-a71a-bfc4df0702ed)@1d3f70ae, httpResponse=HTTP/1.1 200 , securityContext=org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext@1bb1b80b} auth - 2024-02-15 02:59:01,360 DEBUG [qtp1492875057-31] [io.jans.as.server.service.RedirectionUriService] (RedirectionUriService.java:162) - Comparing https://oc.h.usky.io/oidc-callback.html == https://oc.h.usky.io/oidc-callback.html auth - 2024-02-15 02:59:01,360 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:58) - Checking scopes policy for: [openid, profile, email] auth - 2024-02-15 02:59:01,361 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:91) - Granted scopes: [openid, profile, email] auth - 2024-02-15 02:59:01,403 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:58) - Checking scopes policy for: [openid, profile, email] auth - 2024-02-15 02:59:01,404 DEBUG [qtp1492875057-31] [jans.as.server.model.authorize.ScopeChecker] (ScopeChecker.java:91) - Granted scopes: [openid, profile, email]

moabu commented 7 months ago

Hey @tailnet-h-usky-io ,

Thanks for the detailed issue description !

We will take a look at it and see if we can spot the issue and provide the fix.

moabu commented 7 months ago

Just to be sure @tailnet-h-usky-io . Are you using Nginx ? If so have you configured that yet by enabling CORS

nginx.ingress.kubernetes.io/enable-cors: "true"

You can add that annotation in the values.yaml used to install your setup using helm .

tailnet-h-usky-io commented 7 months ago

Hi @moabu !

Thanks very much for the prompt reply and tip. I am using the Nginx ingress option, and did not know about the annotation. I have now added it to the auth server's Ingress resource.

Unfortunately applying it to the janssen-nginx-ingress-auth-server did not fix the problem for me. I deleted it and recreated it with the annotation, just to make sure the change would be picked up.

Does this look right?

❯ kubectl get ing janssen-nginx-ingress-auth-server -o yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    meta.helm.sh/release-name: janssen
    meta.helm.sh/release-namespace: jans
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/proxy-next-upstream: error timeout invalid_header
      http_500 http_502 http_503 http_504
    nginx.org/ssl-services: auth-server
  creationTimestamp: "2024-02-16T14:03:24Z"
  generation: 1
  labels:
    app: janssen-nginx-ingress-auth-server
    app.kubernetes.io/managed-by: Helm
  name: janssen-nginx-ingress-auth-server
  namespace: jans
  resourceVersion: "3340544"
  uid: b588e5af-f95c-45f3-8dd9-c5740b87e27c
spec:
  ingressClassName: nginx
  rules:
  - host: sso.h.usky.io
    http:
      paths:
      - backend:
          service:
            name: auth-server
            port:
              number: 8080
        path: /jans-auth
        pathType: Prefix
  tls:
  - hosts:
    - '*.h.usky.io'
    secretName: comodo-wildcard-ssl-cert
status:
  loadBalancer:
    ingress:
    - ip: 127.0.0.1

I'm still getting the same problem.

image

I can't tell which of the following scenarios is true:

  1. The OwnCloud callback page odic-callback, which renders the HTML page in the screenshot, is configured to disallow all Cross-site requests, and therefore the request to Janssen for the auth token is blocked at the browser (I don't see logs in the auth server for the auth token, so this might be possible) ; or
  2. The Janssen server is receiving - but denying - the auth token request. I think this must be true, because Chrome's debugger does show a status code 401, a Remote Address, and a set of Response Headers for the auth token request.

Just to confirm, scenario 1 indicates that a config change is needed in OwnCloud, while scenario 2 indicates that a config change is needed in Janssen, right?

tailnet-h-usky-io commented 7 months ago

Update: after reading up on how CORS works, and more importantly, a post on how to integrate OwnCloud with a couple of different OIDC providers, I've found and fixed the problem.

I learned that the OIDC Web client for OwnCloud makes simple requests without credentials. I had configured the client'sAuthn Method token endpoint in Janssen with client_seret_basic

image

After adding the annotations you described above, and changing the Authn Method token endpoint in Janssen to none, the request for the OIDC authorization token succeeds. Yay!

image

Also, the next OIDC request – to /jans-auth/restv1/userinfo – also succeeds. :-) , and returns:

{
    "sub": "16301a10-7903-4a5c-9445-d32c6372dcbd",
    "zoneinfo": "America/Los_Angeles",
    "name": "testuser",
    "nickname": "testuser",
    "given_name": "Test",
    "locale": "en-US",
    "inum": "16301a10-7903-4a5c-9445-d32c6372dcbd",
    "family_name": "User",
    "preferred_language": "en",
    "email": "testuser@h.usky.io"
}

According to the browser debugger, a CORS "pre-flight" request was made for the user info, indicating to me that CORS setup is probably OK now.

Unfortunately (for me), login is still not succeeding completely. But since the failed request is to the OwnCloud setup, not the Janssen server, I'm closing this ticket, as it seems the Janssen CORS setup is now working.

Thanks again for your help, @moabu ! I'm sure I'll pop up again here as I (struggle to) integrate Janssen with more systems...

moabu commented 7 months ago

Thank you @tailnet-h-usky-io sorry the detailed updates as that helps our community . We look forward for any further questions you might have.

ossdhaval commented 7 months ago

@moabu and @tailnet-h-usky-io Beautiful and detailed conversation here. :+1:

@moabu Do you think any aspect of this discussion (for example your recommendation about nginx configuration) or what @tailnet-h-usky-io found as root-cause OwnCloud makes simple requests without credentials, can be made part of our documentation. May be as a FAQ? or a new doc about troubleshooting frequently faced integration issues?