Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
|
767e0cf9ae | |
|
59ff04dbbc | |
|
5a2083aef0 | |
|
d272cfd026 | |
|
c1b41684c5 | |
|
77f4d32386 | |
|
2343eb2000 | |
|
5cc3dcad9f | |
|
43c2abf138 | |
|
9d813bfd43 | |
|
29dd3bb2bb | |
|
918cc5713d | |
|
f0bbfa9bb2 | |
|
33ebf1dd0a | |
|
b47d5b2bbe | |
|
65699b2d46 | |
|
7a1644b9e3 | |
|
6fa4c7dbe5 |
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
|||
module luiz.tec.br/cauth
|
||||
module git.luiz.tec.br/luiz/cauth
|
||||
|
||||
go 1.23
|
||||
|
||||
|
|
26
handlers.go
26
handlers.go
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -27,12 +26,21 @@ func NewHandler(oauth2Config *oauth2.Config, session SessionStorer, verifier *oi
|
|||
}, nil
|
||||
}
|
||||
|
||||
type Identity struct {
|
||||
UserID string `json:"userId"`
|
||||
ProviderName string `json:"providerName"`
|
||||
ProviderType string `json:"providerType"`
|
||||
}
|
||||
|
||||
type UserClaims struct {
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
Name string `json:"given_name"`
|
||||
Username string `json:"cognito:username"`
|
||||
Picture string `json:"picture"`
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
Name string `json:"given_name"`
|
||||
Username string `json:"cognito:username"`
|
||||
Picture string `json:"picture"`
|
||||
Sub string `json:"sub"`
|
||||
Groups []string `json:"cognito:groups"`
|
||||
Identities []*Identity `json:"identities"`
|
||||
}
|
||||
|
||||
func generateState() (string, error) {
|
||||
|
@ -47,19 +55,21 @@ func generateState() (string, error) {
|
|||
func (h *Handlers) SignIn(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := generateState()
|
||||
if err != nil {
|
||||
log.Println("Failed to generate state")
|
||||
log.Printf("Failed to generate state: %v", err)
|
||||
http.Error(w, "Something went wrong", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := h.session.Get(r)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get session: %v", err)
|
||||
http.Error(w, "Failed to get session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
session.Values["state"] = state
|
||||
err = session.Save(r, w)
|
||||
if err != nil {
|
||||
log.Printf("Failed to save session: %v", err)
|
||||
http.Error(w, "Failed to save session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -73,6 +83,7 @@ func (h *Handlers) CallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
session, err := h.session.Get(r)
|
||||
if err != nil {
|
||||
log.Printf("Failed to verify ID Token: %v", err)
|
||||
http.Error(w, "Failed to get session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -120,7 +131,6 @@ func (h *Handlers) CallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||
session.Values["access_token"] = oauth2Token.AccessToken
|
||||
session.Values["user_info"] = claims
|
||||
|
||||
fmt.Println(claims)
|
||||
session.Options.MaxAge = int(oauth2Token.Expiry.Sub(time.Now()).Seconds())
|
||||
err = session.Save(r, w)
|
||||
if err != nil {
|
||||
|
|
|
@ -2,10 +2,10 @@ package cauth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
@ -40,25 +40,25 @@ func (m *Middleware) AddUserInfo(next http.Handler) http.Handler {
|
|||
return
|
||||
}
|
||||
userInfo := session.Values["user_info"]
|
||||
fmt.Println(userInfo)
|
||||
|
||||
ctx := context.WithValue(r.Context(), userContextKey, userInfo)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// ProtectedRoute Checks if session and token are present, if not return a 401 response
|
||||
func (m *Middleware) ProtectedRoute(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := m.s.Get(r)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/signin", http.StatusSeeOther)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
token := session.Values["access_token"]
|
||||
|
||||
if token == "" || token == nil {
|
||||
http.Redirect(w, r, "/signin", http.StatusSeeOther)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,52 @@ func (m *Middleware) ProtectedRoute(next http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
// ProtectedRouteWithRedirect Checks if session and token are present, if not return a redirect to /login
|
||||
func (m *Middleware) ProtectedRouteWithRedirect(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := m.s.Get(r)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
token := session.Values["access_token"]
|
||||
|
||||
if token == "" || token == nil {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// IsAdmin Checks if admin group is present
|
||||
func IsAdmin(groups []string) bool {
|
||||
for _, group := range groups {
|
||||
if group == "admin" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AdminProtectedRoute Checks if user is member of admin group, if not return forbidden
|
||||
func (m *Middleware) AdminProtectedRoute(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
userOptional := r.Context().Value(userContextKey)
|
||||
if userOptional != nil {
|
||||
user := userOptional.(UserClaims)
|
||||
if IsAdmin(user.Groups) {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
})
|
||||
}
|
||||
|
||||
func GetUserFromContext(r *http.Request) *UserClaims {
|
||||
userOptional := r.Context().Value(userContextKey)
|
||||
if userOptional != nil {
|
||||
|
@ -73,5 +119,5 @@ func GetUserFromContext(r *http.Request) *UserClaims {
|
|||
return &user
|
||||
}
|
||||
|
||||
return &UserClaims{}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@ package cauth
|
|||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/rbcervilla/redisstore/v9"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/oauth2"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const SESSION_NAME = "auth-session"
|
||||
|
@ -21,7 +22,7 @@ type RedisSession struct {
|
|||
type RedisSessionParams struct {
|
||||
RedisAddress string
|
||||
RedisPassword string
|
||||
//SessionSecret []byte
|
||||
// SessionSecret []byte
|
||||
}
|
||||
|
||||
type SessionStorer interface {
|
||||
|
@ -31,6 +32,7 @@ type SessionStorer interface {
|
|||
func NewRedisSessionStore(params RedisSessionParams) (SessionStorer, error) {
|
||||
gob.Register(&oauth2.Token{})
|
||||
gob.Register(oidc.IDToken{})
|
||||
gob.Register(Identity{})
|
||||
gob.Register(UserClaims{})
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: params.RedisAddress,
|
||||
|
|
Loading…
Reference in New Issue