lot of new functionalities
This commit is contained in:
141
core/utils/identification/oidc.go
Executable file
141
core/utils/identification/oidc.go
Executable file
@@ -0,0 +1,141 @@
|
||||
package identification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/services"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type ContextKey string
|
||||
|
||||
const IdtokenKey ContextKey = "idtoken"
|
||||
const ClaimsKey ContextKey = "claims"
|
||||
|
||||
type IdentificationProvider struct {
|
||||
SessionsStore sessions.Store
|
||||
Provider *oidc.Provider
|
||||
OAuth2Config oauth2.Config
|
||||
TokenVerifier *oidc.IDTokenVerifier
|
||||
Services *services.ServicesHandler
|
||||
}
|
||||
|
||||
func NewIdentificationProvider(cfg *viper.Viper, services *services.ServicesHandler, kv storage.KVHandler) (*IdentificationProvider, error) {
|
||||
var (
|
||||
providerURL = cfg.GetString("identification.oidc.provider")
|
||||
clientID = cfg.GetString("identification.oidc.client_id")
|
||||
clientSecret = cfg.GetString("identification.oidc.client_secret")
|
||||
redirectURL = cfg.GetString("identification.oidc.redirect_url")
|
||||
sessionsSecret = cfg.GetString("identification.sessions.secret")
|
||||
)
|
||||
|
||||
provider, err := oidc.NewProvider(context.Background(), providerURL)
|
||||
if err != nil {
|
||||
var (
|
||||
issuerUrl = cfg.GetString("identification.oidc.provider_config.issuer_url")
|
||||
authUrl = cfg.GetString("identification.oidc.provider_config.auth_url")
|
||||
tokenUrl = cfg.GetString("identification.oidc.provider_config.token_url")
|
||||
userInfoUrl = cfg.GetString("identification.oidc.provider_config.user_info_url")
|
||||
jwksUrl = cfg.GetString("identification.oidc.provider_config.jwks_url")
|
||||
algorithms = []string{"RS256"}
|
||||
)
|
||||
if issuerUrl == "" || authUrl == "" || tokenUrl == "" || jwksUrl == "" {
|
||||
return nil, err
|
||||
}
|
||||
providerConfig := oidc.ProviderConfig{
|
||||
IssuerURL: issuerUrl,
|
||||
AuthURL: authUrl,
|
||||
TokenURL: tokenUrl,
|
||||
UserInfoURL: userInfoUrl,
|
||||
JWKSURL: jwksUrl,
|
||||
Algorithms: algorithms,
|
||||
}
|
||||
|
||||
provider = providerConfig.NewProvider(context.Background())
|
||||
}
|
||||
|
||||
oauth2Config := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectURL,
|
||||
|
||||
// Discovery returns the OAuth2 endpoints.
|
||||
Endpoint: provider.Endpoint(),
|
||||
|
||||
// "openid" is a required scope for OpenID Connect flows.
|
||||
Scopes: []string{oidc.ScopeOpenID, "groups", "first_name", "last_name", "display_name", "email"},
|
||||
}
|
||||
|
||||
store := storage.NewSessionStore(kv, []byte(sessionsSecret))
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: oauth2Config.ClientID})
|
||||
|
||||
return &IdentificationProvider{
|
||||
SessionsStore: store,
|
||||
Provider: provider,
|
||||
OAuth2Config: oauth2Config,
|
||||
TokenVerifier: verifier,
|
||||
Services: services,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *IdentificationProvider) Middleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := p.SessionsStore.Get(r, "parcoursmob_session")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("")
|
||||
}
|
||||
|
||||
if session.Values["idtoken"] == nil || session.Values["idtoken"] == "" {
|
||||
state, err := newState()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
session.Values["state"] = state
|
||||
session.Save(r, w)
|
||||
url := p.OAuth2Config.AuthCodeURL(state)
|
||||
http.Redirect(w, r, url, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
idtoken, err := p.TokenVerifier.Verify(context.Background(), session.Values["idtoken"].(string))
|
||||
if err != nil {
|
||||
state, err := newState()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
session.Values["state"] = state
|
||||
delete(session.Values, "idtoken")
|
||||
http.Redirect(w, r, p.OAuth2Config.AuthCodeURL(state), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
var claims map[string]any
|
||||
|
||||
err = idtoken.Claims(&claims)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("")
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), IdtokenKey, idtoken)
|
||||
ctx = context.WithValue(ctx, ClaimsKey, claims)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func newState() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
Reference in New Issue
Block a user