Merge pull request 'Add authmw for other services to use as middleware' (#1) from authmw into master
Reviewed-on: #1
This commit is contained in:
		
							
								
								
									
										92
									
								
								authmw/token.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								authmw/token.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | package authmw | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"crypto/x509" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"slices" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"git.t-juice.club/microfilm/auth" | ||||||
|  | 	"github.com/golang-jwt/jwt/v5" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func VerifyToken(authURL string, permittedRoles []string) func(http.Handler) http.Handler { | ||||||
|  | 	// Fetch current pubkey | ||||||
|  | 	url := fmt.Sprintf("%s/key", authURL) | ||||||
|  | 	req, err := http.NewRequest(http.MethodGet, url, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp, err := http.DefaultClient.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
|  | 	var authResponse auth.PubkeyResponse | ||||||
|  | 	decoder := json.NewDecoder(resp.Body) | ||||||
|  | 	if err := decoder.Decode(&authResponse); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Parse pubkey | ||||||
|  | 	pub, err := x509.ParsePKIXPublicKey(authResponse.PubKey) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fn := func(next http.Handler) http.Handler { | ||||||
|  | 		fn := func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			authHeader := r.Header.Get("Authorization") | ||||||
|  | 			if !strings.Contains(authHeader, "Bearer ") { | ||||||
|  | 				// No token, pass if unathorized in permitted | ||||||
|  | 				// else reject | ||||||
|  | 				if slices.Contains[[]string, string](permittedRoles, auth.RoleUnauthorized) { | ||||||
|  | 					next.ServeHTTP(w, r) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// Reject and write error response | ||||||
|  | 				w.WriteHeader(http.StatusUnauthorized) | ||||||
|  | 				var errResp auth.ErrorResponse | ||||||
|  | 				errResp.Message = fmt.Sprintf("Authorization required: %s", strings.Join(permittedRoles, ",")) | ||||||
|  | 				errResp.Status = http.StatusUnauthorized | ||||||
|  |  | ||||||
|  | 				encoder := json.NewEncoder(w) | ||||||
|  | 				_ = encoder.Encode(&errResp) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Validate token | ||||||
|  | 			tokenString := strings.Split(authHeader, " ")[1] | ||||||
|  | 			token, err := jwt.ParseWithClaims(tokenString, &auth.MicrofilmClaims{}, func(t *jwt.Token) (interface{}, error) { return pub, nil }) | ||||||
|  | 			if err != nil { | ||||||
|  | 				// Reject and write error response | ||||||
|  | 				w.WriteHeader(http.StatusUnauthorized) | ||||||
|  | 				var errResp auth.ErrorResponse | ||||||
|  | 				errResp.Message = fmt.Sprintf("Token verification failed: %s", err) | ||||||
|  | 				errResp.Status = http.StatusUnauthorized | ||||||
|  |  | ||||||
|  | 				encoder := json.NewEncoder(w) | ||||||
|  | 				_ = encoder.Encode(&errResp) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Add claims to request context | ||||||
|  | 			if claims, ok := token.Claims.(*auth.MicrofilmClaims); ok && token.Valid { | ||||||
|  | 				ctx := context.WithValue(r.Context(), "claims", claims) | ||||||
|  | 				next.ServeHTTP(w, r.WithContext(ctx)) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			next.ServeHTTP(w, r) | ||||||
|  | 		} | ||||||
|  | 		return http.HandlerFunc(fn) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fn | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								token.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								token.go
									
									
									
									
									
								
							| @@ -3,8 +3,9 @@ package auth | |||||||
| import "github.com/golang-jwt/jwt/v5" | import "github.com/golang-jwt/jwt/v5" | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	RoleUser  = "user" | 	RoleUnauthorized = "" | ||||||
| 	RoleAdmin = "admin" | 	RoleUser         = "user" | ||||||
|  | 	RoleAdmin        = "admin" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type MicrofilmClaims struct { | type MicrofilmClaims struct { | ||||||
|   | |||||||
| @@ -1,3 +1,3 @@ | |||||||
| package auth | package auth | ||||||
|  |  | ||||||
| const Version = "v0.1.0" | const Version = "v0.1.1" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user