diff --git a/README.md b/README.md index 643faff5..f3af110c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Vouch Proxy supports many OAuth login providers and can enforce authentication t * [AWS Cognito](https://github.com/vouch/vouch-proxy/issues/105) * Keycloak * [OAuth2 Server Library for PHP](https://github.com/vouch/vouch-proxy/issues/99) +* [HomeAssistant](https://developers.home-assistant.io/docs/en/auth_api.html) * most other OpenID Connect (OIDC) providers Please do let us know when you have deployed Vouch Proxy with your preffered IdP or library so we can update the list. diff --git a/config/config.yml_example_homeassistant b/config/config.yml_example_homeassistant new file mode 100644 index 00000000..4d5c3caa --- /dev/null +++ b/config/config.yml_example_homeassistant @@ -0,0 +1,33 @@ +# vouch config +# bare minimum to get vouch running with HomeAssistant + +vouch: + # logLevel: debug + logLevel: info + + # domains: + # valid domains that the jwt cookies can be set into + # the callback_urls will be to these domains + domains: + - yourdomain.com + + # set allowAllUsers: true to use Vouch Proxy to just accept anyone who can authenticate at the configured provider + allowAllUsers: false + + # whiteList - (optional) allows only the listed usernames + # usernames are usually email addresses (google, most oidc providers) or login/username for github and github enterprise + # using static value for HomeAssistant + whiteList: + - homeassistant + + # Setting publicAccess: true will accept all requests, even without a cookie. + publicAccess: false + +oauth: + # HomeAssistant Auth + # https://developers.home-assistant.io/docs/en/auth_api.html + provider: homeassistant + client_id: https://vouch.yourdomain.com + callback_url: https://vouch.yourdomain.com/auth + auth_url: https://homeassistant.yourdomain.com/auth/authorize + token_url: https://homeassistant.yourdomain.com/auth/token diff --git a/handlers/handlers.go b/handlers/handlers.go index 0a9c3f3b..41cd9c9c 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -524,6 +524,10 @@ func getUserInfo(r *http.Request, user *structs.User, customClaims *structs.Cust if err != nil { return err } + if cfg.GenOAuth.Provider == cfg.Providers.HomeAssistant { + ptokens.PAccessToken = providerToken.Extra("access_token").(string) + return getUserInfoFromHomeAssistant(r, user, customClaims) + } ptokens.PAccessToken = providerToken.AccessToken ptokens.PIdToken = providerToken.Extra("id_token").(string) log.Debugf("ptokens: %+v", ptokens) @@ -705,6 +709,13 @@ func getUserInfoFromIndieAuth(r *http.Request, user *structs.User, customClaims return nil } +// More info: https://developers.home-assistant.io/docs/en/auth_api.html +func getUserInfoFromHomeAssistant(r *http.Request, user *structs.User, customClaims *structs.CustomClaims) (rerr error) { + // Home assistant does not provide an API to query username, so we statically set it to "homeassistant" + user.Username = "homeassistant" + return nil +} + type adfsTokenRes struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go index a71382c0..995cec1a 100644 --- a/pkg/cfg/cfg.go +++ b/pkg/cfg/cfg.go @@ -86,11 +86,12 @@ type oauthConfig struct { // OAuthProviders holds the stings for type OAuthProviders struct { - Google string - GitHub string - IndieAuth string - ADFS string - OIDC string + Google string + GitHub string + IndieAuth string + HomeAssistant string + ADFS string + OIDC string } type branding struct { @@ -121,11 +122,12 @@ var ( // Providers static strings to test against Providers = &OAuthProviders{ - Google: "google", - GitHub: "github", - IndieAuth: "indieauth", - ADFS: "adfs", - OIDC: "oidc", + Google: "google", + GitHub: "github", + IndieAuth: "indieauth", + HomeAssistant: "homeassistant", + ADFS: "adfs", + OIDC: "oidc", } // RequiredOptions must have these fields set for minimum viable config @@ -330,14 +332,14 @@ func BasicTest() error { case GenOAuth.ClientID == "": // everyone has a clientID return errors.New("configuration error: oauth.client_id not found") - case GenOAuth.Provider != Providers.IndieAuth && GenOAuth.Provider != Providers.ADFS && GenOAuth.Provider != Providers.OIDC && GenOAuth.ClientSecret == "": + case GenOAuth.Provider != Providers.IndieAuth && GenOAuth.Provider != Providers.HomeAssistant && GenOAuth.Provider != Providers.ADFS && GenOAuth.Provider != Providers.OIDC && GenOAuth.ClientSecret == "": // everyone except IndieAuth has a clientSecret // ADFS and OIDC providers also do not require this, but can have it optionally set. return errors.New("configuration error: o`auth.client_secret not found") case GenOAuth.Provider != Providers.Google && GenOAuth.AuthURL == "": // everyone except IndieAuth and Google has an authURL return errors.New("configuration error: oauth.auth_url not found") - case GenOAuth.Provider != Providers.Google && GenOAuth.Provider != Providers.IndieAuth && GenOAuth.Provider != Providers.ADFS && GenOAuth.UserInfoURL == "": + case GenOAuth.Provider != Providers.Google && GenOAuth.Provider != Providers.IndieAuth && GenOAuth.Provider != Providers.HomeAssistant && GenOAuth.Provider != Providers.ADFS && GenOAuth.UserInfoURL == "": // everyone except IndieAuth, Google and ADFS has an userInfoURL return errors.New("configuration error: oauth.user_info_url not found") }