package main import ( "context" "encoding/base64" "fmt" "github.com/coreos/go-oidc/v3/oidc" log "github.com/sirupsen/logrus" "golang.org/x/oauth2" "net/http" ) type OidcClams struct { Email string `json:"email"` Profile string `json:"profile"` Username string `json:"preferred_username"` } type OAuth2 struct { LogoutUrl string `json:"end_session_endpoint"` _ctx context.Context _oauth2Config *oauth2.Config _oidcVerifier *oidc.IDTokenVerifier _oidcProvider *oidc.Provider _nonces map[string]string } func (o *OAuth2) GetClaims(w http.ResponseWriter, token *oidc.IDToken) (*OidcClams, error) { var claims = new(OidcClams) err := token.Claims(&claims) if err != nil { return nil, err } return claims, nil } func (o *OAuth2) checkOAuth(w http.ResponseWriter, r *http.Request, allowRedirect bool) (*oidc.IDToken, bool) { cookie, err := r.Cookie("id_token") if err == nil { log.Info("got id_token cookie") token, err := o._oidcVerifier.Verify(o._ctx, cookie.Value) if err != nil { http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) return nil, false } dst, _ := base64.URLEncoding.DecodeString(cookie.Value) fmt.Printf(string(dst)) return token, true } else { //no Bearer available if code := r.URL.Query().Get("code") ; code != "" { state := r.URL.Query().Get("state") //check if nonce is known by server if _, ok := o._nonces[state]; ok == false { return nil, false } delete(o._nonces, state) oauth2Token, err := o._oauth2Config.Exchange(o._ctx, code) if err != nil { http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) return nil, false } rawIDToken, ok := oauth2Token.Extra("id_token").(string) if !ok { http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError) return nil, false } token, err2 := o._oidcVerifier.Verify(o._ctx, rawIDToken) if err2 != nil { http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) return nil, false } idCookie := http.Cookie{ Name: "id_token", Value: rawIDToken, Domain: "", Expires: token.Expiry, Secure: false, HttpOnly: true, Path: "/", SameSite: http.SameSiteLaxMode, } http.SetCookie(w, &idCookie) return token, true } else { //no auth code and no bearer -> redirect if allowRedirect { pw, _ , _ := generateToken(); o._nonces[pw] = pw http.Redirect(w, r, o._oauth2Config.AuthCodeURL(pw), http.StatusFound) } return nil, false } } } func CreateOAuth2(config *Config) (*OAuth2, error) { ctx := context.Background() provider, err := oidc.NewProvider(ctx, config.IssuerUrl) if err != nil { return nil, err } oidcConfig := &oidc.Config{ ClientID: config.OAuth2ClientID, } // Configure an OpenID Connect aware OAuth2 client. verifier := provider.Verifier(oidcConfig) oauthConfig := oauth2.Config{ ClientID: config.OAuth2ClientID, ClientSecret: config.OAuth2ClientSecret, Endpoint: provider.Endpoint(), RedirectURL: config.OAuth2RedirectUrl, Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } ret := OAuth2{ _ctx: ctx, _oauth2Config: &oauthConfig, _oidcVerifier: verifier, _oidcProvider: provider, _nonces: make(map[string]string), } err = provider.Claims(&ret) return &ret, nil }