Giter Site home page Giter Site logo

travisghansen / external-auth-server Goto Github PK

View Code? Open in Web Editor NEW
326.0 9.0 44.0 2.51 MB

easy auth for reverse proxies

License: MIT License

Shell 0.17% Dockerfile 0.19% JavaScript 63.77% Starlark 34.23% C++ 1.12% Smarty 0.52%
oauth oauth2 ldap ldap-authentication htpasswd reverse-proxy traefik traefik-ingress nginx nginx-ingress

external-auth-server's Introduction

Image Image

external-auth-server

eas (pronounced eez) is primarily focused on lowering the barrier to using various authentication schemes in a kubernetes environment (but it works with any reverse proxy supporting external/forward auth). eas can be deployed once and protect many services using disperse authentication methods and providers. The goal is to make enabling authentication as easy as:

  1. generating a new config_token (see below)
  2. configuring the reverse proxy to use the service for external authentication
  3. benefit

Authentication Plugins

Various authentication plugins are supported. Within a single config_token you can enable as many as you would like which results in a pipeline of authentication mechanisms being invoked. The first plugin to result in a 2XX response code will allow the request to be serviced. If all plugins fail, then by default the result from the final plugin defined in the config_token will be returned to the client. You can however alter that on a service-by-service basis by setting the fallback_plugin=plugin index (0 indexed) parameter on the authentication URL.

  • htpasswd
  • LDAP
  • OpenID Connect
  • oauth2
  • request param
  • request header
  • request js
  • jwt
  • firebase jwt

Features

  • works with any proxy server (traefik, nginx, ambassador, istio, envoy, etc) that supports forward/external auth
  • works with any OpenID Connect/oauth2 provider (tested predominantly with keycloak but it should be agnostic)
  • only requires 1 installation to service any number of providers/configurations/vhosts/domains
  • passes tokens to the backing service via headers
  • automatically refreshes tokens
  • server-side config_tokens CONFIG_TOKENS

Usage

If running multiple instances (HA) you will need a shared cache/store (see redis below). You only really need redis if:

  1. You are running HA
  2. You are using the oidc or oauth2 plugins

Refer to the HOWTO for a more detailed overview.

Prerequisites

oauth2 and oidc

  • eas must be able to access OIDC Provider

  • user-agent must be able to access OIDC Provider

  • user-agent must be able to access proxy

  • user-agent must be able to access eas (if redirect_uri is directly pointing to eas service /oauth/callback endpoint)

  • proxy must be able to access eas

  • proxy must send X-Forwarded-Host (localhost:8000) to eas in sub-request

  • proxy must send X-Forwarded-Uri (/anything/foo/bar?test=foo) to eas in sub-request

  • proxy must send X-Forwarded-Proto (http) to eas in sub-request

  • proxy should send X-Forwarded-Method (GET) to eas in sub-request

  • proxy must return non 2XX responses from eas to browser

  • proxy may forward 2XX auth header X-Id-Token to backing service

  • proxy may forward 2XX auth header X-Userinfo to backing service

  • proxy may forward 2XX auth header X-Access-Token to backing service

  • proxy may forward 2XX auth header Authorization to backing service

Launch the server

source

EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
EAS_ISSUER_SIGN_SECRET="super secret" \
EAS_ISSUER_ENCRYPT_SECRET="blah" \
EAS_COOKIE_SIGN_SECRET="hello world" \
EAS_COOKIE_ENCRYPT_SECRET="something" \
EAS_SESSION_ENCRYPT_SECRET="baz" \
EAS_CONFIG_TOKEN_STORES='{}' \
EAS_LOG_LEVEL="info" \
EAS_PORT=8080 \
node src/server.js

docker

docker run -d --name eas -p 8080:8080 \
-e EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
-e EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
-e EAS_ISSUER_SIGN_SECRET="super secret" \
-e EAS_ISSUER_ENCRYPT_SECRET="blah" \
-e EAS_COOKIE_SIGN_SECRET="hello world" \
-e EAS_COOKIE_ENCRYPT_SECRET="something" \
-e EAS_SESSION_ENCRYPT_SECRET="baz" \
-e EAS_CONFIG_TOKEN_STORES='{}' \
-e EAS_LOG_LEVEL="info" \
-e EAS_PORT=8080 \
travisghansen/external-auth-server

Kubernetes

A helm chart is supplied in the repo directly. Reviewing values.yaml is highly recommended as examples are provided for common use-cases.

helm repo add eas https://travisghansen.github.io/external-auth-server
helm repo update
helm upgrade \
--install \
--namespace=external-auth-server \
\
--set configTokenSignSecret=<random> \
--set configTokenEncryptSecret=<random> \
--set issuerSignSecret=<random> \
--set issuerEncryptSecret=<random> \
--set cookieSignSecret=<random> \
--set cookieEncryptSecret=<random> \
--set sessionEncryptSecret=<random> \
--set logLevel="info" \
\
--set redis-ha.enabled=true \
--set redis-ha.auth=true \
--set redis-ha.redisPassword=53c237 \
\
--set storeOpts.store=ioredis \
--set storeOpts.password=53c237 \
--set storeOpts.name=mymaster \
--set storeOpts.sentinels[0].host=eas-redis-ha-announce-0 \
--set storeOpts.sentinels[0].port=26379 \
--set storeOpts.sentinels[1].host=eas-redis-ha-announce-1 \
--set storeOpts.sentinels[1].port=26379 \
--set storeOpts.sentinels[2].host=eas-redis-ha-announce-2 \
--set storeOpts.sentinels[2].port=26379 \
--set storeOpts.keyPrefix="eas:" \
\
--set ingress.enabled=true \
--set ingress.hosts[0]=eas.example.com \
--set ingress.paths[0]=/ \
eas eas/external-auth-server

Generate a token

# please edit the values in bin/generate-config-token.js to your situation
# ie: issuer disovery URL, client_id, client_secret, etc
# also make sure to use the same secrets used when launching the server
EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
node bin/generate-config-token.js

# alternatively you may use the following to create tokens
# files can be either json or yaml
cat config-token.json | docker run --rm -i -e EAS_CONFIG_TOKEN_SIGN_SECRET=foo -e EAS_CONFIG_TOKEN_ENCRYPT_SECRET=bar travisghansen/external-auth-server generate-config-token
cat config-token.json | EAS_CONFIG_TOKEN_SIGN_SECRET=foo EAS_CONFIG_TOKEN_ENCRYPT_SECRET=bar npm run generate-config-token

Configure your reverse proxy

# See full examples in the ./examples/ directory
# particularly nginx has some particular requirements
# NOTE: run over https in production
# NOTE: take care to NOT authenticate `eas` with itself (this is particularly
# possible to happen in service mesh scenarios), whatever tool you use should
# ensure access to the `eas` service bypasses authentication thereby avoiding
# recursive behavior

# traefik
address = http://<eas server ip>:8080/verify?config_token=<token output from above>

# nginx (see examples/nginx.conf)
proxy_pass "http://<eas server ip>:8080/verify?redirect_http_code=401&config_token=<token output from above>";

# ingress-nginx (see examples/ingress-nginx.yaml)

# nginx-ingress-controller (see examples/nginx-ingress-controller.yaml)

# traefik ingress
ingress.kubernetes.io/auth-type: forward
ingress.kubernetes.io/auth-url: "https://eas.example.com/verify?config_token=CONFIG_TOKEN_HERE"
ingress.kubernetes.io/auth-response-headers: X-Userinfo, X-Id-Token, X-Access-Token, Authorization

# ambassador (see file in examples directory)

# istio (see file in examples directory)

# haproxy-ingress (see file in examples directory)

# contour (see file in examples directory)

# envoy (see file in examples directory)

Endpoints

Configure the external auth URL to point to the services /verify endpoint. The URL supports the following query params:

  • config_token=the encrypted configuration token
  • redirect_http_code=code (only use with nginx to overcome external auth module limitations (should be set to 401), otherwise omitted)
  • fallback_plugin=plugin index if all plugins fail authentication which plugin response should be returned to the client

If your provider does not support wildcards you may expose eas directly and set the config_token redirect_uri to the eas service at the /oauth/callback path.

Additional ENV vars

  • EAS_SSL_CERT path to ssl cert file to enable https
  • EAS_SSL_KEY path to ssl key file to enable https
  • EAS_GRPC_ADDRESS the address to start the grpc server on (default is 0.0.0.0)
  • EAS_GRPC_PORT port the grpc server is bound to (default is 50051)
  • EAS_GRPC_SSL_CERT path to ssl cert file to enable https
  • EAS_GRPC_SSL_KEY path to ssl key file to enable https
  • EAS_ALLOW_EVAL allow for potentially unsafe execution of untrusted code (enables request_js and js query engine)
  • EAS_ALLOW_PLAIN_SERVER_SIDE_TOKENS allows server-side token to be unsigned (ie: store plain json/yaml in the store(s))

redis

ioredis cache adapter

Support for sentinel, see bin/generate-store-opts.js with further options.

EAS_STORE_OPTS='{"store":"ioredis","host":"localhost"}'

redis cache adapter

No support for sentinel currently, see bin/generate-store-opts.js with further options.

EAS_STORE_OPTS='{"store":"redis","host":"localhost"}'

external-auth-server's People

Contributors

autarchprinceps avatar bcha avatar cameronbraid avatar djbgeodan avatar djbulsink avatar juissi-t avatar kevinoconnor7 avatar micnncim avatar mlushpenko avatar nagaflokhu avatar nenadalm avatar phoppe93 avatar placydo avatar runningman84 avatar subreptivus avatar t3mi avatar travisghansen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

external-auth-server's Issues

Comparison to alternatives? Where does this solution fit in?

Unlike Pomerium and Authelia, this project provides no UI. It is to be combined with an external auth provider(eg Keycloak for self-hosting) and a reverse proxy like Traefik or Nginx. It then upon successful authentication, forwards the credentials via headers(I'm not sure if this differs from others providing OAuth2/OIDC redirect flows with an auth subdomain and UI, or third-party SSO provider).

Is that all correct? I haven't dug into the assertions feature or what I think you've called pipelined auth(mixing OAuth with JWT for example and reacting based on what was successfully used/discovered), putting those aside, what are similar projects and are those features what help set EAS apart?

While the project has support for LDAP and OAuth/OIDC, I assume it doesn't help much with projects like BookStack which allow for third-party auth or LDAP to manage users/login accounts to their service. Is EAS able to be of value here? Do I need to request/contribute support to BookStack project to be compatible with the way EAS works? If so what is required?

For a service to work with EAS, if I understand correctly, it must support receiving user auth via header values? Grafana seems to refer to this as an Auth Proxy, as does the support with Discourse. So... Reverse Proxy(redirect) => EAS(forward auth headers on success from auth provider) => Service(autologin)? I think you have referred to this before as Auth Code Flow?(still not familiar with different auth flows yet)


In my case, I am looking at moving from services that manage their own individual accounts and each requiring separate login(or linking to a users preferred SSO provider), to a single auth gateway, where a user can link to an external SSO provider(assuming that's compatible with what I'd like), or have all services share a self-hosted auth provider(eg Keycloak), which for services that support it can all share account data via LDAP?

A user joining the community should only have to signup/register at one location, and only link their account(eg to Google) once, not manually link each service to eventually get a single/seamless login experience across all services provided to the community. I'm not sure how achievable this is, or if EAS is suitable to achieve it. I'm still trying to grasp a good enough understanding about the auth and user management technologies available, and what is required of the third-party services to support this(Grafana and Discourse should be ok from the looks of it, but I'm uncertain about BookStack).

Single server with Docker containers for each service currently. nginx-proxy is used, but will need to be customized or switch to Traefik I think for forward auth support, as with nginx it seems to be a module required at build time(which the image nginx-proxy does not have).


EDIT: Seems I misunderstood the Discourse auth proxy, that's the wrong direction I was asking about, as it's redirecting users to login via Discourse SSO to the intended service url. I had thought originally that it was support for passing auth details to Discourse to login from an external auth service.

/envoy/verify-params-header can cause infinite auth loop

Thanks for this project! It's super useful.

We got really stuck for a little while in an infinite auth loop when trying to use this from an envoy ext_authz filter (similar to your examples: istio example, #23 (comment) ).

The ultimate fix ended up being to set pathPrefix to /envoy/verify-params-header/anythingherewillwork. That makes it correctly match the express route:

app.all("/envoy/verify-params-header/*", async (req, res) => {
(in fact, in hindsight even just an extra / on the path will work)

I'm not sure what made those previous examples work with just /envoy/verify-params-header, maybe something automatically appended to the paths?

I think a good change to make this easier to use, would be to update the route to just /envoy/verify-params-header, but I'm not sure if that's how you intended it to be used.

Here's the full filter we ended up with, in case it's useful:

http_filters:
- name: envoy.ext_authz
config:
    failure_mode_allow: false
    http_service:
        path_prefix: /envoy/verify-params-header/anythingherewillwork
        authorization_request:
          allowed_headers:
            patterns:
            - exact: cookie
            - exact: X-Forwarded-Host
            - exact: X-Forwarded-Method
            - exact: X-Forwarded-Proto
            - exact: X-Forwarded-Uri
          headers_to_add:
          - key: "x-eas-verify-params"
            value: '{"config_token_store_id": "env_token_store", "config_token_id": "token_id_1"}'
        server_uri:
          uri: http://external-auth-server.internal-service.svc.cluster.local
          cluster: ext-authz
          timeout: 10s
        status_on_error:
          code: Forbidden
        with_request_body:
          allow_partial_message: true
          max_request_bytes: 4096
- name: envoy.router
  typed_config: {}

This works excellent for us with OIDC and GCP Identity Platform! ๐ŸŽ‰

Question - do tokens have to be encrypted ?

HI,

Just wondering if token encryption is strictly necessary.

Is it so that you can safely send tokens over a http connection to the auth server, rather than forcing use of https ?

If that is the reason, then could it be possible to support unencrypted tokens when using https to talk to the auth server - in my case when using istio I can use mutual tls

Ambassador setup

I'm interested in evaluating eas for usage with Ambassador API Gateway via its AuthService construct. See below for the auth flow we are envisioning for our public users. We have another flow that we're planning to use for internal apps, but I think this would be a better place to start.

Auth Flow
Public User Auth Flow

  1. User logs in via an identity provider connected to Firebase (Google or Facebook).
  2. Upon successful login, a JWT token is issued and returned.
  3. The token is sent in the X-Auth-Token header with API requests.
  4. All protected endpoints offload authentication requests to eas.
  5. eas validates the token with Firebase.
  6. The token validation result is returned.
  7. The user is allowed or denied access based on the result.

Please let me know where to begin and I'm happy to document the steps taken to contribute back to the project.

Consul K/V store support

Traefik uses Consul KV store to enable clustering/HA. It makes sense to have eas also support Consul. This adds simplicity and reduces overhead on Kubernetes cluster

Feature request: disable encryption for server-side tokens

Hello,

First of all let me thank you for all your work and good product!

Do you have any plans to add an option to not use encryption on server-side tokens?

We're deploying our clusters quite frequently and encryption for tokens isn't always working for us. We're trying to avoid node scripts (for our own reasons), and were using openssl script at first, and python script currently. Rarely we have digital envelope routines errors, after token creation, hence the need of regenerating of tokens.
We're using only server-side tokens. Don't get me wrong, but I don't see much sense of using encryption on server-side tokens.

Looks interesting - Questions...

Hey Travis, saw your repo referenced here and it looks like this might apply to what I'm working on right now, but I'm not totally clear on the problem you're trying to solve.

I've got a docker swarm where I'm going to route all incoming traffic through Traefik, and expose KeyCloak for auth and a bunch of APIs will be protected with the forward auth Traefik supports.

Is this repo just doing the validation of the auth tokens? Or encrypting auth tokens so they're not visible in the outside world? Or supporting multiple identity providers? All the above? None?

I'm confused what the config token is for? Can you clarify a bit?

Please don't take any of the above negatively, the features list checks all the boxes I need to check, but I'm confused on the inner workings and some of the implementation. Any additional info you could provide would be appreciated. I'll keep digging through the code to understand more.

Here's two of my repos I've done POCs with where I think I'm implementing things right around this boundary:
https://github.com/gregberns/forward-auth
https://github.com/gregberns/identity-provider-demo

OAuth refresh tokens not being used

The title is bit overly broad but over the last few days I've been trying to track down a long-standing issue where clients need to login after about an hour or so. It's not clear to me if my setup is just misconfigured or if there's a real bug here, but I wanted to open a thread to investigate. At the very least I think the documentation about refresh tokens needs to better spell out how to configure things.

My current config is (minus some unrelated stuff):

{
  /**
  * how to expire the cookie
  * true = cookies expire will expire with tokens
  * false = cookies will be 'session' cookies
  * num seconds = expire after given number of seconds
  */
  cookie_expiry: 60 * 60 * 24 * 30,

  /**
  * how frequently to refresh userinfo data
  * true = refresh with tokens (assuming they expire)
  * false = never refresh
  * num seconds = expire after given number of seconds
  */
  userinfo_expiry: false,

  /**
  * how long to keep a session (server side) around
  * true = expire with tokenSet (if applicable)
  * false = never expire
  * num seconds = expire after given number of seconds (enables sliding window)
  *
  * sessions become a floating window *if*
  * - tokens are being refreshed
  * or
  * - userinfo being refreshed
  * or
  * - session_expiry_refresh_window is a positive number
  */
  session_expiry: 60 * 60 * 24 * 30,

  /**
  * will re-use the same id (ie: same cookie) for a particular client if a session has expired
  */
  session_retain_id: true,

  /**
  * if the access token is expired and a refresh token is available, refresh
  */
  refresh_access_token: true,

  /**
  * which token (if any) to send back to the proxy as the Authorization Bearer value
  * note the proxy must allow the token to be passed to the backend if desired
  *
  * possible values are access_token, or refresh_token
  */
  authorization_token: "access_token",

  /**
  * fetch userinfo and include as X-Userinfo header to backing service
  * only helpful if your specific provider has been implemented
  */
  fetch_userinfo: true,
}

The reason cookie_expiry and session_expiry are set to the same value is due to #43, but I think you'd basically always want that anyway. It doesn't really max sense to have the cookie ever expire sooner than the session, at least if I understand correctly.

The reason the expiry is 30 days is because that's how long the refresh token I'm provided lasts. The access_token I'm provided is good for 60 minutes.

With all of this configured I'm still seeing users needing to login again after a couple of hours. I am using redis to persist session data so it should tolerate eas being restarted and what not.

My current theory is that refresh works once. So basically after an hour from the initial login the refresh token get used to get a new access_token but then someone isn't persisting properly and an hour after that login fails again. I haven't proven this is the case yet though.

OIDC with dash sign in URL

This issue is about logging in with OIDC. It seems that EAS gets confused if the login URL (appliation URL) contains a dash (#), e.g.:

https://myserver.dummy.org/client#/overzicht?license=cafe&place=amsterdam

If you open that URL, EAS will redirect the browser to the authorization endpoint of the OpenID provider. The redirect URL contains, amongst other things, a "redirect_uri" parameter for the callback and an encoded "state" parameter.

I suspect that the state parameter contains a truncated version of the application URL: it doesn't include the part after the dash (i.e. "#/overzicht?license=cafe&place=amsterdam").

Upon receiving the redirect, the browser opens the redirect URL but then also appends the dash part to it:

redirect-URL + "#/overzicht?license=cafe&place=amsterdam"

Once logged in, the browser will then be redirected to the truncated application URL:

https://myserver.dummy.org/client

I hope someone can confirm that the dash is indeed causing the problem, and if there is any workaround or fix for it.

  • Egon

Bug: No response returned if ldap server is unreachable

On kubernetes, with an ingress.yaml annotation as such:

  annotations:
    # Enable authentication - Forward auth to external-auth-server
    ingress.kubernetes.io/auth-type: forward
    ingress.kubernetes.io/auth-url: http://external-auth-server.external-auth-server.svc.k8s:8080/verify?fallback_plugin=0&config_token=<URL safe config_token> # Inside cluster
    ingress.kubernetes.io/auth-response-headers: X-Userinfo, X-Id-Token, X-Access-Token, Authorization

and an auth pipeline of:

  • htpasswd
  • ldap

when htpasswd auth fails, eas proceeds to ldap. If the ldap server is unreachable, no response is returned, until traefik finally times out after 240s.

eas logs:

// htpasswd is 'myPassword'
$ kubectl logs --namespace=external-auth-server external-auth-server-76b4ffc4f-7mxg6 --follow
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: htpasswd"}
{"service":"external-auth-server","level":"debug","message":"plugin response {\"statusCode\":401,\"statusMessage\":\"\",\"body\":\"\",\"cookies\":[],\"clearCookies\":[],\"headers\":{\"WWW-Authenticate\":\"Basic realm=\\\"external authentication server\\\"\"},\"authenticationData\":{},\"plugin\":{\"server\":{},\"config\":{\"type\":\"htpasswd\",\"htpasswd\":\"foo:$apr1$qHDFfhPC$nITSVHgYbDAK1Y0acGRnY0\\nbar:$apr1$qHDFfhPC$nITSVHgYbDAK1Y0acGRnY0\\n\",\"pcb\":{}}}}"}
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: ldap"}
{"service":"external-auth-server","level":"verbose","message":"parent request info: {\"uri\":\"https://foo.example.com/favicon.ico\",\"parsedUri\":{\"scheme\":\"https\",\"host\":\"foo.example.com\",\"path\":\"/favicon.ico\",\"reference\":\"absolute\"},\"parsedQuery\":{},\"method\":\"GET\"}"}
{"service":"external-auth-server","level":"verbose","message":"LdapAuth connection closed: undefined"}

Cannot POST /oauth/callback

Hi,

I am using your external-auth-server (OIDC plugin only) with NGINX and an IdP that, after login, execute a post to: https://my.domain.com/oauth/callback?state=abc123&session_state=def567&code=my-code

This call fails with error 400 and message

Cannot POST /oauth/callback

How can I solve this?

Idea for token generation - docker entrypoint

Currently token generation requires editing source files.

One idea I may suggest is to add an additional entrypoint into the docker container for token generation and you write the config to stdin and read the generated token from stdout

I'm looking to use this project in a kubernetes cluster with istio, and I think writing a kustomize generator would be a good integration point for token generation - so calling into the docker container from the kustomize plugin seems like a good option

Access-Control-Allow-Origin for HTTP-Response "302 Redirect"

To get eas working with a Angular SPA, Javascript needs to access the HTTP-Response that eas gives to redirect the user in case of a invalid or expired session.

Would it be possible to enable eas to set Access-Control-Allow-Origin to "*".

initial setup

Creating this issue to simply provide a forum for discussing the initial setup.

End-to-end example

I'm trying to wrap my head around what the exact flow with eas would look like, once fully configured with something like GitHub or one of the Google auth providers. Is there an end-to-end demo deployment hosted anywhere?

Using existing Redis on k8s

I am trying to setup a new EAS on k8s. On this cluster I already have redis up and running. When I run the helm install with:

--set redis-ha.enabled=true
--set redis-ha.auth=true
--set redis-ha.redisPassword=[...]

--set storeOpts.store=ioredis
--set storeOpts.password=[...]
--set storeOpts.name=mymaster
--set storeOpts.sentinels[0].host=redis
--set storeOpts.sentinels[0].port=26379
--set storeOpts.keyPrefix="eas:" \

Redis is installed as part of the Helm chart. How do I point EAS to my existing redis without installing Redis together with EAS?

access_type missing for OIDC/OAuth. Token expires after one hour.

Hi

I'm using the oidc auth module to authenticate against Google. My actual problem is, that after an hour I have to re-authenticate against Google. I've read that Google has to be called with the access_type=offline to get a refresh_token. But I haven't found any possibility to add this to the configuration. I also investigated on the request in the Chrome dev tools and this param is missing.

In addition I don't use Redis. But I only run a single node k3s cluster with just one instance of Traefik v2 and external-auth-server. I guess in this case Redis is optional.

I use the following configuration, just to check whether all the other stuff is correct:

let config_token = {
  aud: "mydomain.io",
  eas: {
    plugins: [{
      type: "oidc",
      issuer: {
        discover_url: "https://accounts.google.com/.well-known/openid-configuration",
      },
      client: {
        client_id: "myid.apps.googleusercontent.com",
        client_secret: "mysecret"
      },
      scopes: ["openid", "email", "profile"], // must include openid
    redirect_uri: "https://auth.domain.io/oauth/callback",
    features: {
        cookie_expiry: true,
        userinfo_expiry: true,
        session_expiry: true,
        session_expiry_refresh_window: 60 * 30, // Google's access_token expires within 60min
        session_retain_id: true,
        refresh_access_token: true,
        fetch_userinfo: true,
        introspect_access_token: false, // Not supported by Google
        authorization_token: "access_token"
    },
    assertions: {
        exp: true,
        nbf: true,
        iss: true,
        userinfo: [ {
          query_engine: "jp",
          query: "$.email",
          rule: {
            method: "in",
            value: ["[email protected]"],
            case_insensitive: false
          }
        } ]
    },
    cookie: {
        domain: "mydomain.io", 
    },
    headers: {},
}]
  }
};

Google's access_token has a validity of 60 minutes.

Thank you.
Best Danny

Feature request: Single sign out.

Correct me if im wrong or if this belongs somewhere completely else, feel free to remove this request all together.

As far as i understand, in order to sign out from key cloak you will need:

  • Keycloak url
  • Realm
  • Client secret
  • Token
  • Refresh token

I'm a bit reluctant to start hard coding these variables into my back-end, which ideally shouldn't know anything about the whole SSO circus.

Instead, i would suggest (since the EAS already gets all these informations in the config-token supplied by each request) the creation of either:

  • An endpoint on the EAS to sign as user out from a client
  • A header passed to the EAS that will sign the user out from a client

I'm happy to hear your thoughts on this one :)

NODE_EXTRA_CA_CERTS with "unable to verify the first certificate"

I am trying to get k3d/traefik/dex/eas working, and thus far I was able to setup a working demonstrator with dex on http(80), but now I'm struggling with trying get the same setup working with dex on https(443) with a self-signed cert. I'm getting "unable to verify the first certificate" using the oidc plugin.

k3d version v1.7.0
k3s version v1.17.3-k3s1
helm v3.1.2
stable/dex 2.21.0 (installed from helm)
external-auth-server (git clone sha 750875c)

I'm using external-auth-server/charts/external-auth-server/ along with a values.yaml that embeds what looks like a way to inject a self-signed cert CA:

nodeExtraCaCerts: |-
  -----BEGIN CERTIFICATE-----
  ...
  -----END CERTIFICATE-----

With kubectl exec into the externa-auth-server pod, I can see my cert populated in /tmp/certs/node-extra-ca-certs.crt (and verified as the self-signed cert used in my dex deployment), as well as an environment variable populated:

eas@external-auth-server-84bf99f9fc-r4nqn:~$ printenv | grep -i node-extra
NODE_EXTRA_CA_CERTS=/tmp/certs/node-extra-ca-certs.crt

But eas oidc plugin log still gives

{"code":"UNABLE_TO_VERIFY_LEAF_SIGNATURE","level":"error","service":"external-auth-server","message":"unable to verify the first certificate","stack":"Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
at TLSSocket.emit (events.js:198:13)
at     TLSSocket._finishInit (_tls_wrap.js:636:8)"}    {"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 503"}

What is the correct way to get eas with oidc plugin to trust a dex service with a self-signed cert?

NGINX Configuration for OIDC plugin

Hello,

I am using iidc plugin and my configuration for NGINX is similar to:

    location / {

      auth_request /auth;

      auth_request_set $saved_set_cookie $upstream_http_set_cookie;
      auth_request_set $auth_redirect $upstream_http_location;
      auth_request_set $auth_id_token $upstream_http_x_id_token;
      auth_request_set $auth_userinfo $upstream_http_x_userinfo;
      auth_request_set $auth_access_token $upstream_http_x_access_token;

      error_page 401 = @error401;

      # note headers will NOT show up if variable value is blank
      proxy_set_header X-Id-Token $auth_id_token;
      proxy_set_header X-Userinfo $auth_userinfo;
      proxy_set_header X-Access-Token $auth_access_token;

      proxy_pass https://httpbin.org;
    }

    location /auth {

      internal;
      # $scheme$request_method$host$request_uri"
      # localhost:8000
      proxy_set_header X-Forwarded-Host $http_host;
      # GET
      proxy_set_header X-Forwarded-Method $request_method;
      # http
      proxy_set_header X-Forwarded-Proto $scheme;
      # /anything?foo=bar
      proxy_set_header X-Forwarded-Uri $request_uri;

      proxy_pass_request_body off;
      proxy_set_header Content-Length "";
      proxy_pass "http://127.0.0.1:8080/verify?redirect_http_code=401&config_token=token";
    }

    location @error401 {
      if ($auth_redirect) {

        add_header Set-Cookie $saved_set_cookie;
        return 302 $auth_redirect;
      }
    }
  }

But I have the same problems as #23

Specifically X-Forwarded-Uri is not present in "verify request details" silly log even though in my configuration I have proxy_set_header X-Forwarded-Uri $request_uri;

Have you any suggestion?

Idea: Google Integration via SAML

Do you think it would be possible to integrate an app secured by eas as a SAML app into Googles GSuite?

Here's a start page for the Google SAML side:
https://support.google.com/a/answer/6087519?hl=en

I fiddled around (without knowing what SAML is) and managed to add my app-icon to the google application launcher menu. (But to be honest: my app is not an app to be offered on the Marketplace. Otherwise, we would have done this in the first place. It would just be cool to have a working icon in the launcher.)

image

When I click it, Google sends a post with form-data to mydomain which my app/eas don't handle. Maybe they could? Would be extremely cool.

Here's a screenshot of the network tab:

image
image

LDAP + Traefik

Hi there ,

Such a nice project , something i was looking for a long long time.

I'm trying to handle Basic auth in traefik reverse proxy and forward auth to LDAP AD via your services

Can you please , give me some tips . some help to configure all the stuff ?

I want to use it in a single docker env
dynamicly forward auth via label https://docs.traefik.io/v2.0/middlewares/forwardauth/
Try to allow or not by LDAP valid user , or group mapping .

Thanks you !

human readable reusable auth url

As discussed offline we would really like to use server side tokens and in multiple clusters and applications. Therefore each cluster has unique secrets used by the external auth server. Right now each protected application in each cluster has a completely different auth url. This makes it quite difficult to manage on a large scale.

I would like to have a human readable reusable auth url (where only the base url/domain is different).

Right now the auth url looks like this:

https://eas.${base_domain}/verify?config_token=${config_token_alias_encrypted_uri}

I would like to use the auth url liks this:

https://eas.${base_domain}/verify?config_token_id=team_github_operators&config_token_store_id=primary
https://eas.${base_domain}/verify?config_token_id=team_github_customer
https://eas.${base_domain}/verify?config_token_id=team_github_developers

The config_token_store_id should be optional, primary should be the default value there.

Run generate-config-token.js on runtime

At the moment, it is required to generate a config token before actually using the app.
This token can be used with the app.
In docker environment, it's best practice to be able to provide everything on runtime in 1 step.

Maybe the config_token variable should be extracted from generate-config-token.js to a different file. And that file might be mounted with docker on runtime. The token will be calculated in the entrypoint, just before the app itself runs.

undefined in redirect URL

Trying to make eas work with istio, almost got it working (I will share docs later), but getting undefined appended to redirect URI for some reason, any idea what that may be?

Logs from eas (notice bookinfo.dev.k8s.hal24k.nlundefined):

{"service":"external-auth-server","level":"verbose","message":"parsed state redirect uri: {\"scheme\":\"http\",\"host\":\"bookinfo.dev.k8s.hal24k.nlundefined\",\"path\":\"\",\"reference\":\"absolute\"}"}
{"service":"external-auth-server","level":"verbose","message":"parsed request uri: {\"path\":\"/oauth/callback\",\"query\":\"__eas_oauth_handler__=authorization_callback&code=b06e7a405446796a4ea28f2ab24bf306391745767af1a809215a1017eed9da57&scope=openid%20email%20profile&state=05ee47f92a8f54c2bbdacefeab9691d14b5b2c1b80d2e8ddeeac5f0267f9ad62e4551aa461587aeaf85b295d0d585170745e083ab90d4e39e53bf5e93a456feb204a9af6bb0cea606164fdcab94bc54&session_state=5iumlRGmL2bNBJjetsd9lSLjPHrjZeT5Uwor_TfkGaM.6b769f0d8d051e6b9c81221e413f82a1\",\"reference\":\"relative\"}"}
{"service":"external-auth-server","level":"verbose","message":"parsed redirect uri: {\"scheme\":\"http\",\"host\":\"bookinfo.dev.k8s.hal24k.nlundefined\",\"path\":\"\",\"query\":\"__eas_oauth_handler__=authorization_callback&code=b06e7a405446796a4ea28f2ab24bf306391745767af1a809215a1017eed9da57&scope=openid%20email%20profile&state=05ee47f92a8f54c2bbdacefeab9691d14b5b2c1b80d2e8ddeeac5f0267f9ad62e4551aa461587aeaf85b295cd8cd6b63e8af7e76060fb34f00f21efc08d5dd4995635828e53bf5e93a456feb204a9af6bb0cea606164fdcab94bc54&session_state=5iumlRGmL2bNBJjetsd9lSLjPHrjZeT5Uwor_TfkGaM.6b769f0d8d051e6b9c81221e413f82a1\",\"reference\":\"absolute\"}"}
{"service":"external-auth-server","level":"info","message":"redirecting browser to: \"http://bookinfo.dev.k8s.hal24k.nlundefined/?__eas_oauth_handler__=authorization_callback&

Somewhere here, but not sure exactly what is the problem

"parsed state redirect uri: %j",

Allow passing unwrapped Userinfo headers

Currently X-Userinfo can be passed through to applications so that they have data about the logged in user. This data is a JSON-encoded blob.

However, I have some applications (ex. Grafana) that I can only configure to read a header (I can choose the name) that is either a username or e-mail; I cannot configure it to do any JSON parsing and unwrapping.

Would it be possible to add configuration that allows some Userinfo properties to be unwrapped into their own headers?

Traefik setup with Google ends in 503

I have setup Traefik and eas based on the Howto. I have the authentication with Google oauth working. Request comes back to eas server, it then forwards to the original URL but that fails with a 503 code.

The original URL webserver POD does not get the request. For some reason the 503 is generated by eas server. It looks like the succesfull authentication is not recognized.

{"message":"starting verify pipeline","level":"info","service":"external-auth-server"}
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: oauth2"}
{"service":"external-auth-server","level":"info","message":"redirecting to original resource: https://<original-URL>/"}
{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 302"}
{"message":"starting verify pipeline","level":"info","service":"external-auth-server"}
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: oauth2"}
{"level":"error","service":"external-auth-server","message":"Cannot read property 'data' of undefined","stack":"TypeError: Cannot read property 'data' of undefined\n    at OauthPlugin.prepare_token_headers (/home/eas/app/src/plugin/oauth/index.js:790:30)\n    at OauthPlugin.verify (/home/eas/app/src/plugin/oauth/index.js:670:24)\n    at process._tickCallback (internal/process/next_tick.js:68:7)"}
{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 503"}

How can I debug the error and figure out the solution?

Feature Request: Limit user info

We use this config for several ingress configurations:

# traefik ingress
ingress.kubernetes.io/auth-type: forward
ingress.kubernetes.io/auth-url: "https://eas.example.com/verify?config_token=CONFIG_TOKEN_HERE"
ingress.kubernetes.io/auth-response-headers: X-Userinfo, X-Id-Token, X-Access-Token, Authorization

Recently we got this kind of error messages from nginx and apache servers:

400 Bad Request
Request Header Or Cookie Too Large
nginx/1.17.7

It turns out that users which belong to several github organisations have a lot of data in the user info part. From my point of view it would be great to get a limited Info block which would could the loginname, userid, email address, mfa,

I checked the headers and the userinfo part alone is about 4k bytes for my user...

Fallback option

Right now I use GitHub with oauth. In case of an internet outage or GitHub problem I would like to have a fallback to htpasswd or IP based auth.

Is this already possible?

Setting up traefix, eas and keycloak trows the error "unable to verify the first certificate"

I really like the concept of eas and how easy it makes to integrate security for different k8s apps that we will run. So I'm trying to set it up.

I'm using GKS with traefik as my ingress handler. I've been successful in setting up my apps, traefik and eas using basic auth as a first test. This setup works fine.

The final step that I want to take is to add keycloak to the setup. So I have also deployed keycloak into my GKS cluster. I've configured a realm and a client and created config_token.

When I now test the token I get a 503 error back from eas. Looking in the log I see this error:

code: "UNABLE_TO_VERIFY_LEAF_SIGNATURE"
level: "error"
message: "unable to verify the first certificate"
service: "external-auth-server"
stack: "Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1055:34)
at TLSSocket.emit (events.js:198:13)
at TLSSocket._finishInit (_tls_wrap.js:633:8)"

traefik is configured with a TLS wildcard cert for the domain I'm using. The cert works fine in Firefox and Chrome (it a cert issued by godaddy). Both, the apps, eas and keycloak are using a domain covered by the wildcard cert.

What could be going wrong here? Do I need to give a CA to eas, but I don't see how to configure it.

Feature request - oidc assertions on access_token

I use keycloak for oidc and keycloak currently returns the realm roles in the access token

I would like to be able to assert that the realm roles contains a value

If I can only do this for the id_token, I will have to configure an additional mapper in keycloak for each client and map the realm roles into the id token

Keycloak OIDC Error

The following is my setup-
Screen Shot 2020-03-12 at 4 32 35 PM

For the most part the redirects seems to be working. It fails on the external auth server retrieving tokens.

{"service":"external-auth-server","level":"verbose","message":"audMD5: 8708765772d4ac341e8f85effde0c452"}
{"service":"external-auth-server","level":"verbose","message":"cookie name: _eas_oauth_session"}
{"service":"external-auth-server","level":"verbose","message":"decoded state: {\"request_uri\":\"http://nexus.sandbox.xxxx.com/\",\"aud\":\"8708765772d4ac341e8f85effde0c452\",\"csrf\":\"c8f2c9ca-27b5-4818-a2ed-9692d32b2133\",\"req\":{\"headers\":{}},\"request_is_xhr\":false,\"iat\":1583963627}"}
{"service":"external-auth-server","level":"verbose","message":"audMD5: 8708765772d4ac341e8f85effde0c452"}
{"service":"external-auth-server","level":"verbose","message":"cookie name: _eas_oauth_session"}
{"message":"begin token fetch with authorization code","level":"verbose","service":"external-auth-server"}
{"service":"external-auth-server","level":"verbose","message":"compare_redirect_uri: https://eas.sandbox.xxxxx.com/oauth/callback"}
{"message":"failed to retrieve tokens","level":"verbose","service":"external-auth-server"}
{"level":"error","service":"external-auth-server","message":"Unexpected token P in JSON at position 0","stack":"SyntaxError: Unexpected token P in JSON at position 0\n    at JSON.parse (<anonymous>)\n    at authenticatedPost.then.then.response (/home/eas/app/node_modules/openid-client/lib/client.js:801:43)\n    at process._tickCallback (internal/process/next_tick.js:68:7)"}

keycloak client settings-
Screen Shot 2020-03-12 at 4 37 46 PM

Here's the config_token:

{
        type: "oidc",
        issuer: {
          discover_url: http://keycloak.xxxx.com/auth/realms/master/.well-known/uma2-configuration,
        },
        client: {
          client_id: "eas",
          client_secret: "xxxx"

        },
        scopes: ["openid", "email", "profile"], // must include openid
        redirect_uri: "https://eas.xxxx.com/oauth/callback",
        features: {
          cookie_expiry: false,

          userinfo_expiry: true,

          session_expiry: true,
          session_expiry_refresh_window: 86400,

          session_retain_id: true,

          refresh_access_token: true,

          fetch_userinfo: true,

          introspect_access_token: false,

          authorization_token: "access_token"
        },
        assertions: {
          exp: true,

          nbf: true,

          iss: true,

        },
        cookie: {
          domain: "xxxx.com", //defaults to request domain, could do sso with more generic domain
        },
        headers: {},
      }

Feature proposal: dynamic server_token_id propagation from request URL

It's been a long time since the last issue =D

We did another hack to your source code and want to discuss if you think something similar to our hack may be turned into a feature. I will try to explain and would like to hear your thoughts about this. I know it may be too specific to our use-case but let's see.

Last successful setup:

  1. Single eas server
  2. Many envoy filters where we specify token_id per tenant which has to be retrieved from redis. Each filter is applied to istio sidecar, so to specific app, not in general to all publicly exposed services behind ingress.
  3. Single redirect_url for all tokens

Ignoring the why, we thought it would be great to retrieve token dynamically from request subdomain (or URL path for that matter). Then, we can use single filter on ingress level to handle different tokens (token name = tenant name), not on specific sidecar of the service.

So, my colleague added this piece of code to server.js:

if (easVerifyParams.config_token_regex) {
      let matches = req.get("host").match(new RegExp(easVerifyParams.config_token_regex))
      if (matches && matches[1]) {
        easVerifyParams.config_token_id = matches[1]
      } else {
        externalAuthServer.logger.error("config_token_regex: unexpected number of matches (%j)", matches)
      }
    }

and then we changed envoyfilter config to this:

headers_to_add:
- key: x-eas-verify-params
   value: '{"config_token_store_id":"primary", "config_token_regex": ".*\\.(.*)\\.k8s.*"}'

Basically, we are using regexp to get token name from URL. Then, you can split applications (or tenants, or users) with a single filter if some part or URL matches config_token_id.

The last important bit is that we had a single redirect URL for all tokens like https://istio-eas.hal24k.nl/oauth/callback

It caused a problem like this:

  1. During initial request, starting verify pipeline logic was working fine because request host was https://jupyterhub.tenant-354.k8s.dimension.ws/hub/spawn and we could capture regexp group.
  2. After logging in to OIDC, the host header would be https://istio-eas.hal24k.nl/oauth/callback and starting verify pipeline would not extract config_token_id and the rest would fail.

So, while configuring token, we use specific redirect URL per tenant and point all of them to eas service. The only reason for that is to get tenant name in the host header.

So, current working set up:

  1. Modified eas server
  2. Single envoy filter on ingress level with token_id_regexp
  3. Many redirect_url per tenant with include tenant name in the host (but could be url path as well)

Please let us know if you find this interesting and maybe you have better idea how to set this up. So, this could be like another case for config token logic - to use regexp.

Get undefined in location URI after Keycloak authentication

Hi @travisghansen,

I'm trying to get eas working in the following architecture (each one are containers):

  • eas
  • a simple python demo for testing purposes
  • keycloak
  • traefikv2

Here my eas configuration:

let config_token = {
  eas: {
    plugins: [
      {
        type: "oidc",
        issuer: {
          discover_url: "https://auth.example.com/auth/realms/demo/.well-known/openid-configuration"
        },
        client: {
          client_id: 'eas',
          client_secret: 'xxxx',
        },
        scopes: ['openid', 'email', 'profile'],
        custom_authorization_paremeters: {},
        redirect_uri: 'https://eas.example.com/oauth/callback',
        features: {
          cookie_expiry: false,
          userinfo_query: true,
          session_expiry: true,
          session_expiry_refresh_windows: 86400,
          session_retain_id: true,
          authorization_token: 'access_token',
          fetch_userinfo: true,
        },
        assertions: {
          exp: true,
          nbf: true,
          iss: true,
          userinfo: []
        },
        xhr: {},
        cookie: {},
        custom_error_headers: {},
        custom_service_headers: {},
      }
    ]
  }
}

The demo backend is declared with following labels in traefik:

      - "traefik.enable=true"
      - "traefik.http.routers.demo.rule=Host(`demo.example.com`)"
      - "traefik.http.routers.demo.entryPoints=https"
      - "traefik.http.routers.demo.tls=true"
      - "traefik.http.routers.demo.tls.certResolver=letsencrypt"
      - "traefik.http.routers.demo.middlewares=eas-gitlab@file"
      - "traefik.http.services.demo.loadbalancer.server.port=8000"
      - "traefik.http.services.demo.loadbalancer.server.scheme=http"

And the middleware:

http:
  middlewares:
    eas-gitlab:
      forwardAuth:
        trustForwardHeader: true
        address: "https://eas.example.com/verify?config_token=xxx"

Once I'm logged in keycloak, I'm redirect to eas.example.com, and the get a response with following header:

location: https://eas.example.comundefined/?__eas_oauth_handler__=authorization_callback&code=xxx&session_state=yy&state=xxx

I first had a look at #23 but I'm must confess I'm not sure to understand the source issue. As far as I can tell, all users requests received by eas include X-Forward headers (but not in traefik requests to /verify off course):

X-Forwarded-For: xxxx
X-Forwarded-Host: eas.example.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: b1dbda6b3efc

Here some eas debug logs where undefined appears for the first time:

eas_1         | verbose: parent request info: {"uri":"https://eas.example.comundefined","parsedUri":{"scheme":"https","host":"eas.example.comundefined","path":"","reference":"absolute"},"parsedQuery":{}}                                                              
eas_1         | verbose: audMD5: 3b37aad6a3106ebb7e1bf3ff6f33e857
eas_1         | verbose: cookie name: _eas_oauth_session

Can you confirm the missing part should be the requested URI?

Thanks!

ambassador and oidc (identity server 4)

Reference #16 (comment)

Here are my eas logs (basically empty):

kubectl logs eas-external-auth-server-7479497c4c-lfr2q            

> [email protected] start /home/eas/app
> node --nouse-idle-notification --expose-gc --max-old-space-size=8192 src/server.js

{"service":"external-auth-server","level":"debug","message":"cache opts: {\"store\":\"memory\",\"max\":0,\"ttl\":0}"}
{"service":"external-auth-server","level":"info","message":"revoked JTIs: []"}
{"service":"external-auth-server","level":"info","message":"starting server on port 8080"}

Could it be that token is wrong in a way and that's why ambassador can't talk to eas service?

here is my token config:

const jwt = require("jsonwebtoken");
const utils = require("../src/utils");

const config_token_sign_secret =
  process.env.EAS_CONFIG_TOKEN_SIGN_SECRET ||
  utils.exit_failure("missing EAS_CONFIG_TOKEN_SIGN_SECRET env variable");
const config_token_encrypt_secret =
  process.env.EAS_CONFIG_TOKEN_ENCRYPT_SECRET ||
  utils.exit_failure("missing EAS_CONFIG_TOKEN_ENCRYPT_SECRET env variable");

let config_token = {
  /**
   * future feature: allow blocking certain token IDs
   */
  //jti: <some known value>

  /**
   * using the same aud for multiple tokens allows sso for all services sharing the aud
   */
  //aud: "some application id", //should be unique to prevent cookie/session hijacking, defaults to a hash unique to the whole config
  eas: {
    plugins: [{
      type: "oidc",
      issuer: {
          /**
          * via discovery (takes preference)
          */
          discover_url: "https://dev.hal24k.nl/.well-known/openid-configuration",

          /**
          * via manual definition
          */
          //issuer: 'https://accounts.google.com',
          //authorization_endpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
          //token_endpoint: 'https://www.googleapis.com/oauth2/v4/token',
          //userinfo_endpoint: 'https://www.googleapis.com/oauth2/v3/userinfo',
          //jwks_uri: 'https://www.googleapis.com/oauth2/v3/certs',
      },
      client: {
          /**
          * manually defined (preferred)
          */
          client_id: "k8s_ambassador",
          client_secret: "secretsecret"

          /**
          * via client registration
          */
          //registration_client_uri: "",
          //registration_access_token: "",
      },
      scopes: ["openid", "email", "profile"], // must include openid
      /**
      * static redirect URI
      * if your oauth provider does not support wildcards place the URL configured in the provider (that will return to this proper service) here
      */
      redirect_uri: "https://eas.hal24k.nl:8443/oauth/callback",
      features: {
          /**
          * how to expire the cookie
          * true = cookies expire will expire with tokens
          * false = cookies will be 'session' cookies
          * num seconds = expire after given number of seconds
          */
          cookie_expiry: false,

          /**
          * how frequently to refresh userinfo data
          * true = refresh with tokens (assuming they expire)
          * false = never refresh
          * num seconds = expire after given number of seconds
          */
          userinfo_expiry: true,

          /**
          * how long to keep a session (server side) around
          * true = expire with tokenSet (if applicable)
          * false = never expire
          * num seconds = expire after given number of seconds (enables sliding window)
          *
          * sessions become a floating window *if*
          * - tokens are being refreshed
          * or
          * - userinfo being refreshed
          * or
          * - session_expiry_refresh_window is a positive number
          */
          session_expiry: true,

          /**
          * window to update the session window based on activity if
          * nothing else has updated it (ie: refreshing tokens or userinfo)
          *
          * should be a positive number less than session_expiry
          *
          * For example, if session_expiry is set to 60 seconds and session_expiry_refresh_window value is set to 20
          * then activity in the last 20 seconds (40-60) of the window will 'slide' the window
          * out session_expiry time from whenever the activity occurred
          */
          session_expiry_refresh_window: 86400,

          /**
          * will re-use the same id (ie: same cookie) for a particular client if a session has expired
          */
          session_retain_id: true,

          /**
          * if the access token is expired and a refresh token is available, refresh
          */
          refresh_access_token: true,

          /**
          * fetch userinfo and include as X-Userinfo header to backing service
          */
          fetch_userinfo: true,

          /**
          * check token validity with provider during assertion process
          */
          introspect_access_token: false,

          /**
          * which token (if any) to send back to the proxy as the Authorization Bearer value
          * note the proxy must allow the token to be passed to the backend if desired
          *
          * possible values are id_token, access_token, or refresh_token
          */
          authorization_token: "id_token"
      },
      assertions: {
          /**
          * assert the token(s) has not expired
          */
          exp: true,

          /**
          * assert the 'not before' attribute of the token(s)
          */
          nbf: true,

          /**
          * assert the correct issuer of the token(s)
          */
          iss: true,

          /**
          * custom userinfo assertions
          */
          userinfo: [
              // {
              //     ...
              //     see ASSERTIONS.md for details
              // },
              // {
              //     ...
              // }
          ],

          /**
          * custom id_token assertions
          */
          id_token: [
              // {
              //     ...
              //     see ASSERTIONS.md for details
              // },
              // {
              //     ...
              // }
          ]
      },
      cookie: {
          //name: "_my_company_session",//default is _oeas_oauth_session
          //domain: "example.com", //defaults to request domain, could do sso with more generic domain
          //path: "/",
      },
      // see HEADERS.md for details
      headers: {},
    },], // list of plugin definitions, refer to PLUGINS.md for details
  }
};

config_token = jwt.sign(config_token, config_token_sign_secret);
const conifg_token_encrypted = utils.encrypt(
  config_token_encrypt_secret,
  config_token
);

//console.log("token: %s", config_token);
//console.log("");

console.log("encrypted token (for server-side usage): %s", conifg_token_encrypted);
console.log("");

console.log(
  "URL safe config_token: %s",
  encodeURIComponent(conifg_token_encrypted)
);
console.log("");

Having a hard time configuring this.

Hello.

I'm trying to incorporate this into Traefik v2.0 in combination with Keycloak.
Traefik v2.0 allows configuring for a serperate forward authentication middleware, which then can be attached on a per container basis.

I could then, attach this forward authentication middleware, to each container, allowing for single signon to all my microservices.

However, in Keycloak i have to configure a Base URL, Root URL, etc.
This would mean i could only use one URL ?

how should my Client configuration in my Keycloak realm look like in order to make this work ?
Do i need to proxy the EAS port 8080, and configure that as a Root URL,

Can anyone point me into the right direction ?

Suggestion: multistage Docker builds

Currently the docker image for external-auth-server sits at just over 1.04GB. This can be reduced using multi-staged builds. Here's an initial pass at a Dockerfile for eas:

FROM node:10 AS builder

WORKDIR /root
COPY package*.json ./
RUN npm install

FROM node:10-alpine AS release

# Run as a non-root user
RUN adduser --disabled-password eas \
        && mkdir /home/eas/app \
        && chown -R eas: /home/eas
WORKDIR /home/eas/app
USER eas

COPY --from=builder --chown=eas:eas /root/node_modules ./node_modules
COPY --chown=eas:eas . .

EXPOSE 8080

CMD [ "npm", "start" ]

Using node:10-alpine the resultant image ends up being just under 200MB:

docker images external-auth-server:slim
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
external-auth-server                 slim              bfc66bf5f94c        38 seconds ago      194MB

I haven't tested to see if eas will work with this change, but I thought I'd at least suggest a way to reduce the current eas image by 4/5ths.

Feature request: Password Credentials Flow

Hi,

We like to implement EAS with oidc, because of it's flexibility. In our use case we have web-and and desktop-applications using webservices authenticated by EAS.

Some desktop applications which don't support the Authorization Code Flow, but only basic Authentication . That's why we like to have the "Password credential Flow" supported.

I would like to hear your thoughts on this?

If you're positive about it, I'll give it a try to implement it and create PR.

GitHub oauth

Do you really need a full openid provider or would a GitHub oauth application also work?

Refresh token issues

Hi, Travis, it could totally be an issue on our side with some configuration, but perhaps you could validate our assumptions or spot some bug.

In EAS, we see a number of errors like this (wondering is it normal the decoded token is null, probably not):

{"service":"external-auth-server","level":"debug","message":"refresh_token \"ZwrKulkHValgKfaAl3NpsPUKu6GNoC-Zv8LE\""}
{"service":"external-auth-server","level":"debug","message":"refresh_token decoded null"}

Also, later on, we get this message:

{"message":"tokenSet not refreshed externally","level":"warn","service":"external-auth-server"}

And, finally:

{"code":"ETIMEDOUT","connect":true,"level":"error","service":"external-auth-server","message":"ETIMEDOUT","stack":"Error: ETIMEDOUT\n at Timeout._onTimeout (/home/eas/app/node_modules/request/request.js:849:19)\n at ontimeout (timers.js:436:11)\n at tryOnTimeout (timers.js:300:5)\n at listOnTimeout (timers.js:263:5)\n at Timer.processTimers (timers.js:223:10)"}

{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 401"}

So, initial assumption that we have intermittent connectivity issues to the identity server, so we get a timeout and then 401 from EAS. But those messages with refresh tokens are troubling as well. Would you have any insight/ideas?

And could you confirm that timeout is related to the OIDC provider? Based on this

and this line //TODO: better logic here to detect invalid_grant, etc I guess a bit of info is missing to figure out why the error is happening even though I see you are already doing some specific error checks here
is_redirectable_error(e) {

And, looking through some issues with refresh_tokens that we could see from our identity server logs, I've found this potential solution where token usage is OneTimeOnly versus our current setting or ReUse:
image

Hope that's not too much info, maybe it's just a timeout issue and everything else is irrelevant.

external-auth-server with keycloak and ambassador

Hi,

So my setup is:
Ambassador v: 0.72.0
Keycloak v: 5.0.0
And external-auth-server as middle-ware providing openid-connect authentication to a webservice.

I followed the setup first for the Traefik example, substituting config where it was needed.
My configuration for the external-auth-server values.yaml looks as follows:

configTokenSignSecret: ZQTJMHgsRUYD4vaDJnmutYMU
configTokenEncryptSecret: MAuZtmwxfvcJCabSmhrvcAjv
issuerSignSecret: mEAYPr9bcZk8dFda8T6dBmzK
issuerEncryptSecret: QjjgXyVvVyVyUfBW6ZhFTG3w
cookieSignSecret: 5spCwm2jCtekwEb3G6Hcnav8
cookieEncryptSecret: YwX3gzTZmPNUL9v5RMhwsnZq
sessionEncryptSecret: jMV5WtUCfSme4xx9Qkmu2Jv2
logLevel: "info"
redis-ha:
  enabled: false

In addition i made a couple of changes to the service.yaml to accommodate ambassadors annotation-based configuration.... These looks like this:

  annotations:
    getambassador.io/config: |
      ---
      apiVersion: ambassador/v1
      kind:  AuthService
      name:  authentication
      auth_service: {{ include "external-auth-server.fullname" . }}
      proto: http
      allowed_request_headers:
        - authorization
      include_body:
        max_bytes: 4096
        allow_partial: true
      ---
      apiVersion: ambassador/v1
      kind:  Mapping
      name:  eas_mapping
      prefix: /eas/
      bypass_auth: true
      service: {{ include "external-auth-server.fullname" . }}

I managed to create a CONFIG_TOKEN for the keycloak.
The configuration for the plugin added to generate-config-token.js that i used was:


      {
        type: "oidc",
        issuer: {
            /**
            * via discovery (takes preference)
            */
            discover_url: "http://keycloak.default.svc.cluster.local/auth/realms/master/.well-known/openid-configuration",
  
        },
        client: {
            /**
            * manually defined (preferred)
            */
            client_id: "lightningbadger",
            client_secret: "4dbd29da-5b81-417e-a230-abad914de57e"
    
            /**
            * via client registration
            */
            //registration_client_uri: "",
            //registration_access_token: "",
        },
        scopes: ["openid", "email", "profile"], // must include openid
        /**
        * static redirect URI
        * if your oauth provider does not support wildcards place the URL configured in the provider (that will return to this proper service) here
        */
        redirect_uri: "http://api.lightningbadger.io/eas/oauth/callback",
        features: {
            /**
            * how to expire the cookie
            * true = cookies expire will expire with tokens
            * false = cookies will be 'session' cookies
            * num seconds = expire after given number of seconds
            */
            cookie_expiry: true,
    
            /**
            * how frequently to refresh userinfo data
            * true = refresh with tokens (assuming they expire)
            * false = never refresh
            * num seconds = expire after given number of seconds
            */
            userinfo_expiry: true,
    
            /**
            * how long to keep a session (server side) around
            * true = expire with tokenSet (if applicable)
            * false = never expire
            * num seconds = expire after given number of seconds (enables sliding window)
            *
            * sessions become a floating window *if*
            * - tokens are being refreshed
            * or
            * - userinfo being refreshed
            * or
            * - session_expiry_refresh_window is a positive number
            */
            session_expiry: true,
    
            /**
            * window to update the session window based on activity if
            * nothing else has updated it (ie: refreshing tokens or userinfo)
            *
            * should be a positive number less than session_expiry
            *
            * For example, if session_expiry is set to 60 seconds and session_expiry_refresh_window value is set to 20
            * then activity in the last 20 seconds (40-60) of the window will 'slide' the window
            * out session_expiry time from whenever the activity occurred
            */
            session_expiry_refresh_window: 86400,
    
            /**
            * will re-use the same id (ie: same cookie) for a particular client if a session has expired
            */
            session_retain_id: true,
    
            /**
            * if the access token is expired and a refresh token is available, refresh
            */
            refresh_access_token: true,
    
            /**
            * fetch userinfo and include as X-Userinfo header to backing service
            */
            fetch_userinfo: true,
    
            /**
            * check token validity with provider during assertion process
            */
            introspect_access_token: false,
    
            /**
            * which token (if any) to send back to the proxy as the Authorization Bearer value
            * note the proxy must allow the token to be passed to the backend if desired
            *
            * possible values are id_token, access_token, or refresh_token
            */
            authorization_token: "access_token"
        },
        assertions: {
            /**
            * assert the token(s) has not expired
            */
            exp: true,
    
            /**
            * assert the 'not before' attribute of the token(s)
            */
            nbf: true,
    
            /**
            * assert the correct issuer of the token(s)
            */
            iss: true,
    
            /**
            * custom userinfo assertions
            */
            userinfo: [
            ],
    
            /**
            * custom id_token assertions
            */
            id_token: [
            ]
        },
        cookie: {
            //name: "_my_company_session",//default is _oeas_oauth_session
            //domain: "example.com", //defaults to request domain, could do sso with more generic domain
            //path: "/",
        },
        // see HEADERS.md for details
        headers: {},
    }

However, here comes the part on which i got stuck. The examples provided in this repo uses Traefik configuration and adds this to the

ingress.kubernetes.io/auth-url: https://eas.example.com/verify?fallback_plugin=0&config_token=PLACE_CONFIG_TOKEN_OUTPUT_HERE 

However, since we are using ambassador this is not an option.
So I expect I need to mount the configuration into a secret with the env adapter.
Which i tried without any luck.
So i tried adding the following to my values.yaml

configTokenStores:
 primary:
   adapter: env
   options:
     cache_ttl: 3600
     var: < I HAVE NO IDEA WHAT TO ADD HERE>
configTokens:
 1:  <MY CONFIG TOKEN NOT URL ENCODED >

Can you maybe help me out with this configuration :)
Then i can maybe help with writing some documentation :P

Feature Request: Cookie settings (HttpOnly, Secure)

Hi,

I'm using external-auth-server together with Kubernetes and Traefik v2.1. It's really simple to configure and use.

I had, actually still have one issue which took me hours to find out why it happens exactly.
In my case, I have Home-Assistant as the service which should be protected by external-auth-server. This application is a progressive web app (PWA) which means, it uses service-worker quite heavily.
So, each initial authentication process was working fine (clean browser or incognito tab), because there was no Service Worker in place. After an hour (the token expired), I get redirected to Google for another sign in. But this time the final cookie cannot be set or read and the requests ends in a 503-error.

I guess the issue behind is that the two cookies' set have the flag httpOnly. This disallows the service-worker from getting/setting the cookies. But I'm not absolutely sure whether this is the only problem. But if I tick the option Bypass for network in the Chrome debug tools, then everything is working fine.

Is there already such an option to configure which flags should be set by external-auth-server? If not, would this be a huge task? I know I will lose a bit of security by not setting this flag. But I don't see any other possibility. On the other hand, I'm not very experienced with service-workers.

Thank you.
Best Danny

Getting 503 after google authentication

I decided to open a new issue for this. It's not related to the new changes #51 and #50. The issue was already there.

It could be difficult for you to reproduce this, as it only happens to websites which have a service-worker in place. In my case it's home-assistant.

The very first authentication process in a clean browser window (empty caches or incognito) works always. If I activate on Chrome (desktop) the service-worker setting Bypass for network, then it's working every time, not only at the first attempt.

In every other case if I browse to my endpoint ha.mydomain.com, I get redirected to the google's login page. After login, I get redirected back to auth.mydomain.com and from there to my final website ha.mydomain.com. But on this final site, I always receive a 503 coming from eas. I guess the behaviour has some dependencies to wrong or missing csrf-cookies. Please find bellow the logs in verbose mode. I tried to remove as little information as possible. I guess there are still too many private information inside. The log abstract is from the sub-process after I hit login on google's auth page.

{"service":"external-auth-server","level":"verbose","message":"parsed state redirect uri: {\"scheme\":\"https\",\"host\":\"ha.mydomain.com\",\"path\":\"/\",\"reference\":\"absolute\"}"}
{"service":"external-auth-server","level":"verbose","message":"parsed request uri: {\"path\":\"/oauth/callback\",\"query\":\"state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca&code=4%2FwAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&prompt=none&session_state=9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4\",\"reference\":\"relative\"}"}
{"service":"external-auth-server","level":"verbose","message":"parsed redirect uri: {\"scheme\":\"https\",\"host\":\"ha.mydomain.com\",\"path\":\"/\",\"query\":\"__eas_oauth_handler__=authorization_callback&authuser=0&code=4%2FwAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g&prompt=none&scope=email%20profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&session_state=9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4&state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca\",\"reference\":\"absolute\"}"}
{"service":"external-auth-server","level":"info","message":"redirecting browser to: \"https://ha.mydomain.com/?__eas_oauth_handler__=authorization_callback&authuser=0&code=4%2FwAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g&prompt=none&scope=email%20profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&session_state=9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4&state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca\""}
{"message":"starting verify pipeline","level":"info","service":"external-auth-server"}
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: oidc"}
{"service":"external-auth-server","level":"verbose","message":"parent request info: {\"uri\":\"https://ha.mydomain.com/?__eas_oauth_handler__=authorization_callback&authuser=0&code=4%2FwAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g&prompt=none&scope=email%20profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&session_state=9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4&state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca\",\"parsedUri\":{\"scheme\":\"https\",\"host\":\"ha.mydomain.com\",\"path\":\"/\",\"query\":\"__eas_oauth_handler__=authorization_callback&authuser=0&code=4%2FwAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g&prompt=none&scope=email%20profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&session_state=9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4&state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca\",\"reference\":\"absolute\"},\"parsedQuery\":{\"__eas_oauth_handler__\":\"authorization_callback\",\"authuser\":\"0\",\"code\":\"4/wAEXNukyYa1KIyEmhX67QWDsimepPfAbK4AG9m6L1SZtLsB5jfoPYuHewaOIKlI65XGIii5rr02ORetzvmij47g\",\"prompt\":\"none\",\"scope\":\"email profile https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid\",\"session_state\":\"9bf4db09808cd433cedefc5225a695fcf4fca18d..5df4\",\"state\":\"074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f722b0b126dc2fc62a99f4feccced52130bcf1805568f7523e5a305c6c988c4b152b5b65115e770753694e99f9e46743e31b33b70b22bc58fbe549dcaa447343555a8027fa332b436249bd782268a8316addf3224cd5b3b86f740ddf7551dc5a628d6d6b559fa8f54874b5e0874e2cc00068dbe46c0a4a3e0628e094f85f395d5ae7729e2c263c05edc4d25c6df88ccc6f39c40678b4f5e4830ac5a0a63ca5a795b162dde912a355e4d0ff3a82a1eb2fe29fbe43e5c0f023818f1a2b8f9b331bddade1da04c2699216ab3f6760d76401ca\"},\"method\":\"GET\"}"}
{"service":"external-auth-server","level":"verbose","message":"audMD5: 8dd2fd4e5d13b05256fd8f1cdf42904a"}
{"service":"external-auth-server","level":"verbose","message":"cookie name: _eas_oauth_session"}
{"service":"external-auth-server","level":"verbose","message":"decoded state: {\"request_uri\":\"https://ha.mydomain.com/\",\"aud\":\"8dd2fd4e5d13b05256fd8f1cdf42904a\",\"csrf\":\"667598ee-890e-4777-a164-d459e85be1a8\",\"iat\":1580754168}"}
{"service":"external-auth-server","level":"verbose","message":"audMD5: 8dd2fd4e5d13b05256fd8f1cdf42904a"}
{"service":"external-auth-server","level":"verbose","message":"cookie name: _eas_oauth_session"}
{"message":"mismatched csrf values","level":"verbose","service":"external-auth-server"}
{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 503"}
{"message":"starting verify pipeline","level":"info","service":"external-auth-server"}
{"service":"external-auth-server","level":"info","message":"starting verify for plugin: oidc"}
{"service":"external-auth-server","level":"verbose","message":"parent request info: {\"uri\":\"https://ha.mydomain.com/service_worker.js\",\"parsedUri\":{\"scheme\":\"https\",\"host\":\"ha.mydomain.com\",\"path\":\"/service_worker.js\",\"reference\":\"absolute\"},\"parsedQuery\":{},\"method\":\"GET\"}"}
{"service":"external-auth-server","level":"verbose","message":"audMD5: 8dd2fd4e5d13b05256fd8f1cdf42904a"}
{"service":"external-auth-server","level":"verbose","message":"cookie name: _eas_oauth_session"}
{"service":"external-auth-server","level":"verbose","message":"redirect_uri: https://auth.mydomain.com/oauth/callback"}
{"service":"external-auth-server","level":"verbose","message":"callback redirect_uri: https://accounts.google.com/o/oauth2/v2/auth?client_id=myclientid.apps.googleusercontent.com&scope=openid%20email%20profile&response_type=code&access_type=offline&redirect_uri=https%3A%2F%2Fauth.mydomain.com%2Foauth%2Fcallback&state=074e40bb50c3b25721f56f3b07cd7ab3b53ce383472acfe8286ddef11b7073f0892cd088c3189bba5758d1740bb39e3bd16216d23d206b7e6e5dabe1b181dbea25a63bbec292125525c46fde1914f2f704edb801be09e5658087e4c188bd15b68f74be0c0382e8ce388d18237b4ba74132668303563bc8317b08e334384b666b4851aa0a5a4aa2a2dbaefc293657a1da4f2614f4ff516f6b89e39088652be5214244ae38e1555469931f9a68fa99a4b1486cf1d183ee7d520538b9ca292f112224be5f406f5365cbba7ab6162d05ebc3e6130f52998d909c66424937f40d6f4692d844c3fc18b26e009f07d4a528e670299ba0a806bc1479b26fc7f90d0f68d1182ade3a8c5ec62dfba1fdbe994e1ae53f3af39946a2d8416f331503f32a2ed159254f7e8440dc51513f6d30f798dfc8"}
{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 302"}

oidc introspection not working

Hi Travis,

I would like to use the oidc access token introspection feature for a project. But it is not working when using a discover_url. In the eas log I this error message:

{"message":"issuer does not support introspection","level":"error","service":"external-auth-server"}

In our oidc metadata there is an "introspection_endpoint" provided. This is according correct to https://tools.ietf.org/html/rfc8414 .

I assume it is caused by this line:
https://github.com/travisghansen/external-auth-server/blob/master/src/plugin/oauth/index.js#L1254

I guess it should be

      if (!issuer.metadata.introspection_endpoint) {

Instead of:

      if (!issuer.metadata.token_introspection_endpoint) {

Cannot read property 'data' of undefined

Hi. I installed the server via helm and I am receiving the error from title after ldap login.

Version:

# helm -n auth-server list
NAME       	NAMESPACE  	REVISION	UPDATED                                	STATUS  	CHART                     	APP VERSION
auth-server	auth-server	3       	2020-01-15 10:12:52.779433674 +0100 CET	deployed	external-auth-server-0.1.0	1.0

Relevang logs:

{"service":"external-auth-server","level":"verbose","message":"ldap userinfo: undefined"}
{"level":"error","service":"external-auth-server","message":"Cannot read property 'data' of undefined","stack":"TypeError: Cannot read property 'data' of undefined\n    at LdapPlugin.verify (/home/eas/app/src/plugin/ldap/index.js:115:30)\n    at process._tickCallback (internal/process/next_tick.js:68:7)"}
{"service":"external-auth-server","level":"info","message":"end verify pipeline with status: 503"}

maybe the check:

if (userinfo !== null) {

should be userinfo != null instead of userinfo !== null?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.