From cdf6e686842ab4563afe95df000037772f3096b3 Mon Sep 17 00:00:00 2001
From: totomz <tommaso.doninelli@gmail.com>
Date: Fri, 7 Apr 2023 12:15:06 +0200
Subject: [PATCH] Use multiple oidc claim to find the username

The clim `preferred_username` is optional in Azure AD. Although is listed as preferred, in some enterprise environment it's not possible to add this additional claim. `unique_name` and `upn` are legacy alternatives
---
 cmd/rdpgw/web/oidc.go | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/cmd/rdpgw/web/oidc.go b/cmd/rdpgw/web/oidc.go
index 1a41b01..927f855 100644
--- a/cmd/rdpgw/web/oidc.go
+++ b/cmd/rdpgw/web/oidc.go
@@ -3,10 +3,12 @@ package web
 import (
 	"encoding/hex"
 	"encoding/json"
+	"errors"
 	"github.com/bolkedebruin/rdpgw/cmd/rdpgw/identity"
 	"github.com/coreos/go-oidc/v3/oidc"
 	"github.com/patrickmn/go-cache"
 	"golang.org/x/oauth2"
+	"log"
 	"math/rand"
 	"net/http"
 	"time"
@@ -15,7 +17,6 @@ import (
 const (
 	CacheExpiration = time.Minute * 2
 	CleanupInterval = time.Minute * 5
-	oidcKeyUserName = "preferred_username"
 )
 
 type OIDC struct {
@@ -81,7 +82,15 @@ func (h *OIDC) HandleCallback(w http.ResponseWriter, r *http.Request) {
 	}
 
 	id := identity.FromRequestCtx(r)
-	id.SetUserName(data[oidcKeyUserName].(string))
+
+	userName := findUsernameInClaims(data)
+	if userName == "" {
+		err = errors.New("no odic claim for username found")
+		log.Print(err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+
+	id.SetUserName(userName)
 	id.SetAuthenticated(true)
 	id.SetAuthTime(time.Now())
 	id.SetAttribute(identity.AttrAccessToken, oauth2Token.AccessToken)
@@ -93,6 +102,18 @@ func (h *OIDC) HandleCallback(w http.ResponseWriter, r *http.Request) {
 	http.Redirect(w, r, url, http.StatusFound)
 }
 
+func findUsernameInClaims(data map[string]interface{}) string {
+	candidates := []string{"preferred_username", "unique_name", "upn"}
+	for _, claim := range candidates {
+		userName, found := data[claim].(string)
+		if found {
+			return userName
+		}
+	}
+
+	return ""
+}
+
 func (h *OIDC) Authenticated(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		id := identity.FromRequestCtx(r)
-- 
GitLab