diff --git a/cmd/cmd.go b/cmd/cmd.go index 179be2b..627acf5 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -60,6 +60,7 @@ import ( _ "github.com/sikalabs/slu/cmd/elasticsearch/get" _ "github.com/sikalabs/slu/cmd/example" _ "github.com/sikalabs/slu/cmd/example_server" + _ "github.com/sikalabs/slu/cmd/example_server_oidc" _ "github.com/sikalabs/slu/cmd/expand" _ "github.com/sikalabs/slu/cmd/expand/file" _ "github.com/sikalabs/slu/cmd/expand/string" diff --git a/cmd/example_server_oidc/example_server_oidc.go b/cmd/example_server_oidc/example_server_oidc.go new file mode 100644 index 0000000..275d857 --- /dev/null +++ b/cmd/example_server_oidc/example_server_oidc.go @@ -0,0 +1,93 @@ +package example_server_oidc + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/coreos/go-oidc" + "github.com/sikalabs/slu/cmd/root" + "github.com/spf13/cobra" + "golang.org/x/oauth2" +) + +var FlagPort int +var FlagIssuer string +var FlagClientID string +var FlagClientSecret string + +func init() { + root.RootCmd.AddCommand(Cmd) + Cmd.PersistentFlags().IntVarP(&FlagPort, "port", "p", 8000, "Listen on port") + Cmd.Flags().StringVar(&FlagIssuer, "issuer", "", "Issuer") + Cmd.MarkFlagRequired("issuer") + Cmd.Flags().StringVar(&FlagClientID, "client-id", "", "Client ID") + Cmd.MarkFlagRequired("client-id") + Cmd.Flags().StringVar(&FlagClientSecret, "client-secret", "", "Client Secret") + Cmd.MarkFlagRequired("client-secret") +} + +var Cmd = &cobra.Command{ + Use: "example-server-oidc", + Short: "Run example web server with OIDC", + Args: cobra.NoArgs, + Run: func(c *cobra.Command, args []string) { + Server(FlagPort, FlagIssuer, FlagClientID, FlagClientSecret) + }, +} + +func Server(port int, issuer, clientID, clientSecret string) { + ctx := context.Background() + + provider, err := oidc.NewProvider(ctx, issuer) + if err != nil { + log.Fatal(err) + } + + config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + Endpoint: provider.Endpoint(), + RedirectURL: fmt.Sprintf("http://127.0.0.1:%d/callback", port), + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, config.AuthCodeURL("state"), http.StatusFound) + }) + + http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Query().Get("state") != "state" { + http.Error(w, "state did not match", http.StatusBadRequest) + return + } + + oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code")) + if err != nil { + http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) + return + } + + rawIDToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError) + return + } + + idToken, err := provider.Verifier(&oidc.Config{ClientID: clientID}).Verify(ctx, rawIDToken) + if err != nil { + http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) + return + } + + _ = idToken // ID Token is now verified and can be used + + fmt.Println(rawIDToken) + + fmt.Fprintf(w, "Login successful! %s", rawIDToken) + }) + + fmt.Printf("http://127.0.0.1:%d\n", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) +} diff --git a/go.mod b/go.mod index a6a6f1f..5e9fdfe 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/caarlos0/env/v6 v6.10.1 github.com/cheggaaa/pb/v3 v3.1.5 github.com/cloudflare/cloudflare-go v0.83.0 + github.com/coreos/go-oidc v2.1.0+incompatible github.com/denisenkom/go-mssqldb v0.12.3 github.com/digitalocean/godo v1.102.1 github.com/docker/docker v24.0.9+incompatible @@ -235,6 +236,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect @@ -277,6 +279,7 @@ require ( google.golang.org/grpc v1.61.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/square/go-jose.v2 v2.2.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.24.2 // indirect diff --git a/go.sum b/go.sum index e785319..ce0fbe4 100644 --- a/go.sum +++ b/go.sum @@ -515,6 +515,7 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= @@ -1145,6 +1146,7 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -1859,6 +1861,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=