mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-16 09:38:49 +02:00
170 lines
4.8 KiB
Go
170 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/coreos/go-oidc"
|
|
"github.com/gorilla/mux"
|
|
"golang.org/x/oauth2"
|
|
"gopkg.in/square/go-jose.v2/json"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
baseUrl = "https://keycloak.imn.htwk-leipzig.de/realms/htwkalender-dev" // Keycloak Realm URL
|
|
clientID = "htwkalender-dev" // Client ID
|
|
clientSecret = "pWUPXSSqszCWrFkoWuvdyZIuP9bshOcf" // Client secret
|
|
redirectURL = "http://localhost/callback" // URL to redirect after login
|
|
oidcConfig = oauth2.Config{
|
|
ClientID: clientID,
|
|
ClientSecret: clientSecret,
|
|
Endpoint: oauth2.Endpoint{
|
|
AuthURL: baseUrl + "/oauth/authorize",
|
|
TokenURL: baseUrl + "/oauth/token",
|
|
},
|
|
RedirectURL: redirectURL,
|
|
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
|
}
|
|
)
|
|
|
|
func main() {
|
|
// Create router
|
|
router := mux.NewRouter()
|
|
|
|
// Public and private routes
|
|
router.HandleFunc("/public", publicHandler)
|
|
router.HandleFunc("/private", privateHandler)
|
|
|
|
log.Println("Starting server on :8081")
|
|
log.Fatal(http.ListenAndServe(":8081", router))
|
|
}
|
|
|
|
func publicHandler(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintf(w, "This is public content, no login required!")
|
|
}
|
|
|
|
func privateHandler(w http.ResponseWriter, r *http.Request) {
|
|
// Get bearer token from the request header
|
|
token, err := getBearerToken(r)
|
|
if err != nil {
|
|
http.Error(w, "Unauthorized: No token found", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
// Validate the token using GitLab's token introspection
|
|
active, err := introspectToken(token)
|
|
if err != nil || !active {
|
|
slog.Warn("Failed to introspect token", "error", err)
|
|
http.Error(w, "Unauthorized: Invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
// Get user information
|
|
userInfo, err := getUserInfo(token)
|
|
if err != nil {
|
|
slog.Warn("Failed to get user information", "error", err)
|
|
http.Error(w, "Unauthorized: Unable to retrieve user information", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(w, "Hello, %s! Your email is %s", userInfo.Name, userInfo.Email)
|
|
}
|
|
|
|
func getBearerToken(r *http.Request) (string, error) {
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
return "", fmt.Errorf("Authorization header missing")
|
|
}
|
|
|
|
// Extract token from "Bearer <token>"
|
|
token := strings.TrimPrefix(authHeader, "Bearer ")
|
|
if token == authHeader {
|
|
return "", fmt.Errorf("Malformed authorization header")
|
|
}
|
|
return token, nil
|
|
}
|
|
|
|
func introspectToken(token string) (bool, error) {
|
|
introspectionURL := "https://gitlab.dit.htwk-leipzig.de/oauth/introspect"
|
|
|
|
// Create the request body
|
|
data := url.Values{}
|
|
data.Set("token", token)
|
|
|
|
req, err := http.NewRequest("POST", introspectionURL, strings.NewReader(data.Encode()))
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to create introspection request: %v", err)
|
|
}
|
|
|
|
// Use client credentials for basic auth
|
|
req.SetBasicAuth(clientID, clientSecret)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to introspect token: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Check the status code
|
|
if resp.StatusCode != http.StatusOK {
|
|
return false, fmt.Errorf("introspection request failed with status: %s", resp.Status)
|
|
}
|
|
|
|
var introspectionResponse struct {
|
|
Active bool `json:"active"`
|
|
Error string `json:"error"`
|
|
ErrorDescription string `json:"error_description"`
|
|
}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&introspectionResponse); err != nil {
|
|
return false, fmt.Errorf("failed to parse introspection response: %v", err)
|
|
}
|
|
|
|
// Log the introspection response for debugging
|
|
fmt.Printf("Introspection response: %+v\n", introspectionResponse)
|
|
|
|
return introspectionResponse.Active, nil
|
|
}
|
|
|
|
// New function to get user information from GitLab
|
|
func getUserInfo(token string) (*UserInfo, error) {
|
|
userInfoURL := "https://gitlab.dit.htwk-leipzig.de/oauth/userinfo"
|
|
|
|
req, err := http.NewRequest("GET", userInfoURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create user info request: %v", err)
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user info: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("user info request failed with status: %s", resp.Status)
|
|
}
|
|
|
|
var userInfo UserInfo
|
|
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
|
|
return nil, fmt.Errorf("failed to parse user info response: %v", err)
|
|
}
|
|
|
|
return &userInfo, nil
|
|
}
|
|
|
|
// Struct for user information
|
|
type UserInfo struct {
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
Name string `json:"name"`
|
|
// Add any other fields you need from the user info response
|
|
}
|