Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC #168

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
39 changes: 39 additions & 0 deletions backend/app/api/providers/extractors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package providers

import (
"errors"
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
"net/http"

"github.com/hay-kot/httpkit/server"
Expand Down Expand Up @@ -53,3 +54,41 @@ func getLoginForm(r *http.Request) (LoginForm, error) {

return loginForm, nil
}

func getOAuthForm(r *http.Request) (services.OAuthValidate, error) {
var oauthForm services.OAuthValidate
switch r.Header.Get("Content-Type") {
case "application/x-www-form-urlencoded":
err := r.ParseForm()
if err != nil {
return oauthForm, errors.New("failed to parse form")
}

oauthForm.Issuer = r.PostFormValue("issuer")
oauthForm.Code = r.PostFormValue("code")
oauthForm.State = r.PostFormValue("state")
case "application/json":
err := server.Decode(r, &oauthForm)
if err != nil {
log.Err(err).Msg("failed to decode OAuth form")
return oauthForm, err
}
default:
return oauthForm, errors.New("invalid content type")
}

if oauthForm.Issuer == "" || oauthForm.Code == "" {
return oauthForm, validate.NewFieldErrors(
validate.FieldError{
Field: "iss",
Error: "Issuer is empty",
},
validate.FieldError{
Field: "code",
Error: "Code is missing",
},
)
}

return oauthForm, nil
}
67 changes: 67 additions & 0 deletions backend/app/api/providers/oauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package providers

import (
"context"
"errors"
"fmt"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/rs/zerolog/log"
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
"golang.org/x/oauth2"
"net/http"
"os"
"strings"
)

type OAuthProvider struct {
name string
service *services.OAuthService
config *services.OAuthConfig
}

func NewOAuthProvider(ctx context.Context, service *services.OAuthService, name string) (*OAuthProvider, error) {
upperName := strings.ToUpper(name)
clientId := os.Getenv(fmt.Sprintf("HBOX_OAUTH_%s_ID", upperName))
clientSecret := os.Getenv(fmt.Sprintf("HBOX_OAUTH_%s_SECRET", upperName))
redirectUri := os.Getenv(fmt.Sprintf("HBOX_OAUTH_%s_REDIRECT", upperName))

providerUrl := os.Getenv(fmt.Sprintf("HBOX_OAUTH_%s_URL", upperName))
// TODO: fallback for all variabnles if no well known is supported
if providerUrl == "" {
return nil, errors.New("Provider url not given")
}
provider, err := oidc.NewProvider(ctx, providerUrl)
if err != nil {
return nil, err
}
log.Debug().Str("AuthUrl", provider.Endpoint().AuthURL).Msg("discovered oauth provider")

return &OAuthProvider{
name: name,
service: service,
config: &services.OAuthConfig{
Config: &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
Endpoint: provider.Endpoint(),
RedirectURL: redirectUri,
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
},
Provider: provider,
Verifier: provider.Verifier(&oidc.Config{ClientID: clientId}),
},
}, nil
}

func (p *OAuthProvider) Name() string {
return p.name
}

func (p *OAuthProvider) Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error) {
oauthForm, err := getOAuthForm(r)
if err != nil {
return services.UserAuthTokenDetail{}, err
}

return p.service.Login(r.Context(), p.config, oauthForm)
}
12 changes: 10 additions & 2 deletions backend/app/api/routes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"embed"
"errors"
"fmt"
Expand Down Expand Up @@ -67,12 +68,19 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR

r.Get("/currencies", chain.ToHandlerFunc(v1Ctrl.HandleCurrency()))

providers := []v1.AuthProvider{
providerList := []v1.AuthProvider{
providers.NewLocalProvider(a.services.User),
}
if _, exist := os.LookupEnv("HBOX_OAUTH_OIDC_URL"); exist {
provider, err := providers.NewOAuthProvider(context.Background(), a.services.OAuth, "oidc")
if err != nil {
panic(err)
}
providerList = append(providerList, provider)
}

r.Post("/users/register", chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
r.Post("/users/login", chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...)))
r.Post("/users/login", chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providerList...)))

userMW := []errchain.Middleware{
a.mwAuthToken,
Expand Down
44 changes: 24 additions & 20 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module github.com/sysadminsmedia/homebox/backend
go 1.23.0

require (
ariga.io/atlas v0.19.1
ariga.io/atlas v0.27.0
entgo.io/ent v0.14.1
github.com/ardanlabs/conf/v3 v3.1.8
github.com/containrrr/shoutrrr v0.8.0
github.com/coreos/go-oidc/v3 v3.11.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-playground/validator/v10 v10.22.1
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
Expand All @@ -23,6 +24,7 @@ require (
github.com/yeqown/go-qrcode/v2 v2.2.4
github.com/yeqown/go-qrcode/writer/standard v1.2.4
golang.org/x/crypto v0.31.0
golang.org/x/oauth2 v0.23.0
modernc.org/sqlite v1.33.1
)

Expand All @@ -32,21 +34,22 @@ require (
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-openapi/inflect v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl/v2 v2.19.1 // indirect
github.com/hashicorp/hcl/v2 v2.22.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
Expand All @@ -57,18 +60,19 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/swaggo/files/v2 v2.0.1 // indirect
github.com/yeqown/reedsolomon v1.0.0 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.24.0 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
golang.org/x/image v0.20.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a // indirect
modernc.org/libc v1.60.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
Expand Down
Loading