Initial example authentication to Azure ad
This commit is contained in:
126
main.go
Normal file
126
main.go
Normal file
@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"os"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/microsoft"
|
||||
"github.com/coreos/go-oidc"
|
||||
)
|
||||
|
||||
var (
|
||||
clientID = os.Getenv("AZURE_CLIENT_ID")
|
||||
clientSecret = os.Getenv("AZURE_CLIENT_SECRET")
|
||||
redirectURL = os.Getenv("AZURE_REDIRECT_URL") // e.g., http://localhost:5000/callback
|
||||
tenantID = os.Getenv("AZURE_TENANT_ID")
|
||||
|
||||
provider *oidc.Provider
|
||||
verifier *oidc.IDTokenVerifier
|
||||
oauth2Config *oauth2.Config
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
issuer := fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", tenantID)
|
||||
|
||||
var err error
|
||||
provider, err = oidc.NewProvider(ctx, issuer)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get provider: %v", err)
|
||||
}
|
||||
|
||||
verifier = provider.Verifier(&oidc.Config{ClientID: clientID})
|
||||
|
||||
oauth2Config = &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: microsoft.AzureADEndpoint(tenantID),
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
http.HandleFunc("/", handleIndex)
|
||||
http.HandleFunc("/callback", handleCallback)
|
||||
|
||||
log.Println("Server started at http://localhost:5000")
|
||||
log.Fatal(http.ListenAndServe(":5000", nil))
|
||||
}
|
||||
|
||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
idTokenCookie, err := r.Cookie("id_token")
|
||||
if err != nil {
|
||||
log.Println("No id_token cookie found:", err)
|
||||
http.Redirect(w, r, oauth2Config.AuthCodeURL("state", oauth2.AccessTypeOffline), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := verifier.Verify(r.Context(), idTokenCookie.Value)
|
||||
if err != nil {
|
||||
log.Println("Token verification failed:", err)
|
||||
// Clear the invalid cookie to avoid another loop
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "id_token",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
})
|
||||
fmt.Printf("Tokent: %s", token)
|
||||
http.Redirect(w, r, oauth2Config.AuthCodeURL("state", oauth2.AccessTypeOffline), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
var claims struct {
|
||||
Email string `json:"email"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := token.Claims(&claims); err != nil {
|
||||
http.Error(w, "Failed to parse claims", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
email := claims.Email
|
||||
if email == "" {
|
||||
email = strings.Split(claims.PreferredUsername, "@")[0]
|
||||
}
|
||||
fmt.Fprintf(w, "Welcome! Logged in as: %s", email)
|
||||
}
|
||||
|
||||
func handleCallback(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
token, err := oauth2Config.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
http.Error(w, "Token exchange failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
http.Error(w, "No id_token found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify and set as cookie
|
||||
_, err = verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to verify ID token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Set token as cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "id_token",
|
||||
Value: rawIDToken,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
})
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
Reference in New Issue
Block a user