main.go hinzugefügt
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
keycloakURL = "https://keycloak.example.com/realms/myrealm"
|
||||
|
||||
clientID = "go-demo"
|
||||
clientSecret = "CHANGE_ME"
|
||||
|
||||
redirectURL = "https://app.example.com/callback"
|
||||
)
|
||||
|
||||
var (
|
||||
oauthConfig *oauth2.Config
|
||||
verifier *oidc.IDTokenVerifier
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// OIDC Discovery (.well-known/openid-configuration)
|
||||
provider, err := oidc.NewProvider(ctx, keycloakURL)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
verifier = provider.Verifier(
|
||||
&oidc.Config{
|
||||
ClientID: clientID,
|
||||
},
|
||||
)
|
||||
|
||||
oauthConfig = &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: []string{
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
},
|
||||
}
|
||||
|
||||
http.HandleFunc("/", home)
|
||||
http.HandleFunc("/login", login)
|
||||
http.HandleFunc("/callback", callback)
|
||||
http.HandleFunc("/logout", logout)
|
||||
|
||||
log.Println("running :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
|
||||
// Startet OIDC Login
|
||||
func login(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// CSRF-Schutz: State erzeugen
|
||||
state := randomString()
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "oidc_state",
|
||||
Value: state,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
url := oauthConfig.AuthCodeURL(
|
||||
state,
|
||||
)
|
||||
|
||||
http.Redirect(w, r, url, http.StatusFound)
|
||||
}
|
||||
|
||||
|
||||
// Callback von Keycloak
|
||||
func callback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
stateCookie, _ := r.Cookie("oidc_state")
|
||||
|
||||
if stateCookie == nil ||
|
||||
stateCookie.Value != r.URL.Query().Get("state") {
|
||||
http.Error(w, "invalid state", 400)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
token, err := oauthConfig.Exchange(
|
||||
r.Context(),
|
||||
r.URL.Query().Get("code"),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
|
||||
if !ok {
|
||||
http.Error(w, "missing id_token", 500)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
idToken, err := verifier.Verify(
|
||||
r.Context(),
|
||||
rawIDToken,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "invalid token", 500)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Userinformationen aus Token lesen
|
||||
var claims struct {
|
||||
Username string `json:"preferred_username"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
idToken.Claims(&claims)
|
||||
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "user",
|
||||
Value: claims.Username,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
|
||||
http.Redirect(w, r, "/", 302)
|
||||
}
|
||||
|
||||
|
||||
func home(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
cookie, err := r.Cookie("user")
|
||||
|
||||
if err != nil {
|
||||
|
||||
fmt.Fprint(w, `
|
||||
<h1>OIDC Demo</h1>
|
||||
<a href="/login">
|
||||
<button>Anmeldung über OIDC</button>
|
||||
</a>
|
||||
`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
<h1>Hallo %s</h1>
|
||||
|
||||
<form action="/logout" method="post">
|
||||
<button>Abmelden</button>
|
||||
</form>
|
||||
`, cookie.Value)
|
||||
}
|
||||
|
||||
|
||||
func logout(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "user",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
})
|
||||
|
||||
http.Redirect(w, r, "/", 302)
|
||||
}
|
||||
|
||||
|
||||
// kleiner State Generator
|
||||
func randomString() string {
|
||||
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b)
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
}
|
||||
Reference in New Issue
Block a user