vouch/vouch-proxy

Azure AD support

tomsmyers opened this issue · 10 comments

i'm working on an integration with azure AD. after a successful login, vouch unpacks the body of the OIDC /userinfo response into a structs.User object, which takes values from fields username, email etc.

if err = json.Unmarshal(data, user); err != nil {

when constructing the jwt to store, the claims are constructed using this structs.User.Username.

later when checking the token in /validate, vouch checks if the username is populated and returns 401 if is not.

if claims.Username == "" {

this logic makes the assumption that username is included in the /userinfo response, which in my case it is not. 😞

is there a way to take information like this from the ID token instead of the /userinfo response? in azure AD, i can't configure which fields are included in the /userinfo response (e.g. to add the user's email address), but i can configure which fields are included in the tokens.

vouch config for posterity:

VOUCH_JWT_MAXAGE=20
VOUCH_ALLOWALLUSERS=true
VOUCH_LOGLEVEL=debug
VOUCH_COOKIE_SECURE=false
VOUCH_COOKIE_DOMAIN=localhost

OAUTH_PROVIDER=oidc
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...
OAUTH_BASEURL=https://login.microsoftonline.com/...
OAUTH_AUTH_URL=https://login.microsoftonline.com/.../oauth2/v2.0/authorize
OAUTH_TOKEN_URL=https://login.microsoftonline.com/.../oauth2/v2.0/token
OAUTH_USER_INFO_URL=https://graph.microsoft.com/oidc/userinfo
OAUTH_SCOPES=openid,profile,email
OAUTH_CALLBACK_URLS=http://localhost:9090/auth

@tomsmyers have you tried using the adfs provider? IIRC it uses the same interfaces as Azure AD. A quick view of the adfs.go code shows it falling back to using the UPN if an email isn't present.

https://github.com/vouch/vouch-proxy/blob/master/config/config.yml_example_adfs

unfortunately using the adfs provider does not work with azure AD; you get the error The 'resource' request parameter is not supported. when redirecting to the login page.

@simongottschlag do you have any insight into Azure AD setups? ^^

@tomsmyers if you'd care to add a new provider at providers/azure/azure.go I'd be happy to accept a PR. I'm not a user of Azure so I'm not in a position to test/support Azure AD at this time.

@bnfinet Azure AD has their own quirks compared to ADFS. The resource query parameter is required with ADFS to populate the claims correctly while causing issues with Azure AD.

There are also differences with using Azure AD with a single tenant and using the “multi-tenant” endpoints and may require additional configuration to get both working.

thanks for the comments, i opened a PR to add support for my azure ad config by simply mapping upn claim to username and email (based on the docs, upn is an email address), unless the fields already exist in the token.

#292

@tomsmyers thanks much for the addition to VP!

Has anyone any documentation on how to setup Vouch with Azure AD sitting behind an nginx reverse proxy ? This would be amazing.

@simongottschlag Can I also configure azure_token through environmental variables for OAuth config?

I want to use it with docker compose:

vouch-proxy:
    container_name: ex-vouch
    image: voucher/vouch-proxy:alpine
    restart: unless-stopped
    volumes:
      - nginx-config-data/vouch:/config
    enviroment:
      - VOUCH_DOMAINS=vouch.dockertest1.azurewebsites.net
      - VOUCH_ALLOWALLUSERS=true
      - VOUCH_COOKIE_DOMAIN=dockertest1.azurewebsites.net
      - VOUCH_SESSION_KEY=********
      - OAUTH_PROVIDER=azure
      - OAUTH_CLIENT_ID=123456789
      - OAUTH_CLIENT_SECRET=********
      - OAUTH_AUTH_URL=https://login.microsoftonline.com/.../oauth2/v2.0/authorize
      - OAUTH_TOKEN_URL=https://login.microsoftonline.com/.../oauth2/v2.0/token
      - OAUTH_SCOPES=openid,profile,email
      - OAUTH_CALLBACK_URL=https://dockertest1.azurewebsites.net/auth
      - OAUTH_AZURE_TOKEN=id_token

With reference to #320
I think I am not sure yet, if I really need to set the "OAUTH_AZURE_TOKEN".

Thank you in advance.

@jastlw it should but that may have been overlooked when we implemented config via environmental variables.

Could you please test that, and open a new issue if it does not work?

jastlw commented

@bnfinet I forgot to give the feedback: it is working. :)

      - VOUCH_ALLOWALLUSERS=true
      - VOUCH_COOKIE_DOMAIN=domain.de
      - VOUCH_SESSION_KEY=***
      - OAUTH_PROVIDER=azure
      - OAUTH_CLIENT_ID=********-****-****-****-***************
      - OAUTH_CLIENT_SECRET=**********
      - OAUTH_AUTH_URL=https://login.microsoftonline.com/********-****-****-****-***************/oauth2/v2.0/authorize
      - OAUTH_TOKEN_URL=https://login.microsoftonline.com/********-****-****-****-***************/oauth2/v2.0/token
      - OAUTH_USER_INFO_URL=https://graph.microsoft.com/oidc/userinfo
      - OAUTH_SCOPES=openid,profile,email
      - OAUTH_CALLBACK_URL=https://vouch.domain.de/auth
      - OAUTH_AZURE_TOKEN=id_token
      - VIRTUAL_HOST=vouch.domain.de
      - VIRTUAL_PORT=9090
      - LETSENCRYPT_HOST=vouch.domain.de
      - LETSENCRYPT_EMAIL=admin@domain.de
      - VOUCH_LOGLEVEL=debug

@sintaxx I used nginx-proxy with the following configs as Per-VIRTUAL_HOST and Per-VIRTUAL_HOST location configuration
app.domain.de

# send all requests to the `/validate` endpoint for authorization
auth_request /validate;

location = /validate {
  # forward the /validate request to Vouch Proxy
  proxy_pass http://vouch.domain.de/validate;

  # be sure to pass the original host header
  proxy_set_header Host $http_host;

  # Vouch Proxy only acts on the request headers
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";

  # optionally add X-Vouch-User as returned by Vouch Proxy along with the request
  auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user;

  # these return values are used by the @error401 call
  auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt;
  auth_request_set $auth_resp_err $upstream_http_x_vouch_err;
  auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount;
}

# if validate returns `401 not authorized` then forward the request to the error401 block
error_page 401 = @error401;

location @error401 {
  # redirect to Vouch Proxy for login
  return 302 https://vouch.domain.de/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
}

app.domain.de_location

  # you may need to set these variables in this block as per https://github.com/vouch/vouch-proxy/issues/26#issuecomment-425215810
  # auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user
  # auth_request_set $auth_resp_x_vouch_idp_claims_groups $upstream_http_x_vouch_idp_claims_groups;
  # auth_request_set $auth_resp_x_vouch_idp_claims_given_name $upstream_http_x_vouch_idp_claims_given_name;

  # set user header (usually an email)
  proxy_set_header X-Vouch-User $auth_resp_x_vouch_user;
  # optionally pass any custom claims you are tracking
  # proxy_set_header X-Vouch-IdP-Claims-Groups $auth_resp_x_vouch_idp_claims_groups;
  # proxy_set_header X-Vouch-IdP-Claims-Given_Name $auth_resp_x_vouch_idp_claims_given_name;
  # optionally pass the access token or id token
  # proxy_set_header X-Vouch-IdP-AccessToken $auth_resp_x_vouch_idp_accesstoken;
  # proxy_set_header X-Vouch-IdP-IdToken $auth_resp_x_vouch_idp_idtoken;

Thanks a lot!