diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3030da8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/ssoService.iml b/.idea/ssoService.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/ssoService.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..442abc8 --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module ssoService + +go 1.22 + +require ( + github.com/coreos/go-oidc v2.2.1+incompatible + golang.org/x/oauth2 v0.23.0 +) + +require ( + github.com/pquerna/cachecontrol v0.2.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f22244f --- /dev/null +++ b/go.sum @@ -0,0 +1,22 @@ +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= +github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= +github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..ae9084f --- /dev/null +++ b/main.go @@ -0,0 +1,13 @@ +package ssoService + +import ( + "log" + "net/http" +) + +func main() { + http.HandleFunc("/login", loginHandler) + http.HandleFunc("/callback", callbackHandler) + http.HandleFunc("/user", userHandler) + log.Fatal(http.ListenAndServe(":8090", nil)) +} diff --git a/service/ssoServer.go b/service/ssoServer.go new file mode 100644 index 0000000..0e53a02 --- /dev/null +++ b/service/ssoServer.go @@ -0,0 +1,123 @@ +package service + +import ( + "context" + "fmt" + "golang.org/x/oauth2" + "io/ioutil" + "net/http" + "github.com/coreos/go-oidc" +) + +var cfg = &oauth2.Config{ + ClientID: "client_id", + ClientSecret: "client_secret", + RedirectURL: "http://localhost:8080/callback", + Scopes: []string{"openid", "profile", "email"}, + Endpoint: oauth2.Endpoint{ + AuthURL: "http://localhost:8080/authorize", + TokenURL: "http://localhost:8080/token", + }, +} + +provider, err := oidc.NewProvider(context.Background(), "http://localhost:8080") +if err != nil { +log.Fatalf("Failed to create provider: %v", err) +} + +client := &http.Client{ +Transport: &oauth2.Transport{ +Source: provider.TokenSource(context.Background(), &oauth2.Token{ +AccessToken: accessToken, +}), +}, +} + +func userHandler(w http.ResponseWriter, r *http.Request) { + accessToken := r.URL.Query().Get("access_token") + if accessToken == "" { + http.Error(w, "Missing access token", http.StatusBadRequest) + return + } + + claims, err := verifyJWT(accessToken) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.Write([]byte(fmt.Sprintf("Hello, %s!", claims.Subject))) +} +func loginHandler(w http.ResponseWriter, r *http.Request) { + url := cfg.AuthCodeURL("state", oauth2.AccessTypeOnline) + http.Redirect(w, r, url, http.StatusFound) +} +func callbackHandler(w http.ResponseWriter, r *http.Request) { + code := r.URL.Query().Get("code") + if code == "" { + http.Error(w, "Missing authorization code", http.StatusBadRequest) + return + } + + token, err := cfg.Exchange(context.Background(), code) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + http.SetCookie(w, &http.Cookie{ + Name: "access_token", + Value: token.AccessToken, + }) + + http.Redirect(w, r, "/", http.StatusFound) +} + +func loginHandler(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, cfg.AuthCodeURL("state", oauth2.AccessTypeOnline), http.StatusFound) +} + +func callbackHandler(w http.ResponseWriter, r *http.Request) { + code := r.URL.Query().Get("code") + if code == "" { + http.Error(w, "Missing authorization code", http.StatusBadRequest) + return + } + + token, err := cfg.Exchange(context.Background(), code) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + http.SetCookie(w, &http.Cookie{ + Name: "access_token", + Value: token.AccessToken, + }) + + http.Redirect(w, r, "/", http.StatusFound) +} + +func userHandler(w http.ResponseWriter, r *http.Request) { + accessToken, err := r.Cookie("access_token") + if err != nil { + http.Redirect(w, r, "/login", http.StatusFound) + return + } + + resp, err := http.Get("http://localhost:8081/user?access_token=" + accessToken.Value) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.Write(body) +} \ No newline at end of file