5
5
"fmt"
6
6
"net/http"
7
7
"os"
8
+ "strings"
8
9
9
10
"github.com/golang/glog"
10
11
)
@@ -23,6 +24,9 @@ func RequestHeaderKey() contextKey {
23
24
}
24
25
25
26
const (
27
+ // Authorization header for access tokens (standard OAuth2)
28
+ AuthorizationHeader = "Authorization"
29
+
26
30
// SSE transport header for OCM offline token
27
31
// NOTE: http.Header keys are stored in canonical format, hence the different casing required here.
28
32
// The provided X-OCM-OFFLINE-TOKEN header key will be translated to X-Ocm-Offline-Token
@@ -32,19 +36,61 @@ const (
32
36
StdioTokenEnv = "OCM_OFFLINE_TOKEN"
33
37
)
34
38
35
- // ExtractTokenFromSSE extracts OCM offline token from X-OCM-OFFLINE-TOKEN header
36
- func ExtractTokenFromSSE (headers map [string ]string ) (string , error ) {
37
- glog .V (3 ).Infof ("SSE headers received: %+v" , headers )
39
+ // TokenInfo represents extracted token information
40
+ type TokenInfo struct {
41
+ Token string
42
+ TokenType string // "access" or "offline"
43
+ }
44
+
45
+ // ExtractBearerToken extracts access token from Authorization header
46
+ func ExtractBearerToken (headers map [string ]string ) (string , error ) {
47
+ authHeader , exists := headers [AuthorizationHeader ]
48
+ if ! exists || authHeader == "" {
49
+ return "" , fmt .Errorf ("missing or empty %s header" , AuthorizationHeader )
50
+ }
51
+
52
+ // Check for "Bearer " prefix (case-insensitive)
53
+ const bearerPrefix = "Bearer "
54
+ if len (authHeader ) <= len (bearerPrefix ) {
55
+ return "" , fmt .Errorf ("invalid Authorization header format" )
56
+ }
57
+
58
+ if ! strings .EqualFold (authHeader [:len (bearerPrefix )], bearerPrefix ) {
59
+ return "" , fmt .Errorf ("Authorization header must use Bearer scheme" )
60
+ }
61
+
62
+ token := strings .TrimSpace (authHeader [len (bearerPrefix ):])
63
+ if token == "" {
64
+ return "" , fmt .Errorf ("empty Bearer token" )
65
+ }
66
+
67
+ return token , nil
68
+ }
69
+
70
+ // ExtractTokenInfoFromSSE extracts token info from SSE headers, preferring access tokens
71
+ func ExtractTokenInfoFromSSE (headers map [string ]string ) (* TokenInfo , error ) {
72
+ // Log only header keys for security (never log header values which may contain tokens)
73
+ headerKeys := make ([]string , 0 , len (headers ))
74
+ for key := range headers {
75
+ headerKeys = append (headerKeys , key )
76
+ }
77
+ glog .V (3 ).Infof ("SSE headers received (keys only): %v" , headerKeys )
78
+
79
+ // Try Authorization header first (access token)
80
+ if token , err := ExtractBearerToken (headers ); err == nil {
81
+ glog .V (3 ).Info ("Found access token in Authorization header" )
82
+ return & TokenInfo {Token : token , TokenType : "access" }, nil
83
+ }
38
84
39
- // Try exact header name first
85
+ // Fallback to offline token header
40
86
token , exists := headers [SSETokenHeader ]
41
87
if exists && token != "" {
42
- glog .V (3 ).Infof ("Found OCM token in header %s" , SSETokenHeader )
43
- return token , nil
88
+ glog .V (3 ).Infof ("Found offline token in header %s" , SSETokenHeader )
89
+ return & TokenInfo { Token : token , TokenType : "offline" } , nil
44
90
}
45
91
46
- glog .Warningf ("Missing or empty %s header in SSE request . Available headers : %+ v" , SSETokenHeader , headers )
47
- return "" , fmt .Errorf ("missing or empty %s header" , SSETokenHeader )
92
+ glog .Warningf ("No valid tokens found in SSE headers . Available header keys : %v" , headerKeys )
93
+ return nil , fmt .Errorf ("missing valid authentication token" )
48
94
}
49
95
50
96
// ExtractTokenFromStdio extracts OCM offline token from OCM_OFFLINE_TOKEN environment variable
@@ -56,40 +102,45 @@ func ExtractTokenFromStdio() (string, error) {
56
102
return token , nil
57
103
}
58
104
59
- // ExtractTokenFromContext extracts OCM offline token from context based on transport mode
60
- func ExtractTokenFromContext (ctx context.Context , transport string ) (string , error ) {
105
+ // ExtractTokenInfoFromContext extracts token info from context based on transport mode
106
+ func ExtractTokenInfoFromContext (ctx context.Context , transport string ) (* TokenInfo , error ) {
61
107
glog .V (2 ).Infof ("Extracting token for transport mode: %s" , transport )
62
108
63
109
switch transport {
64
110
case "stdio" :
65
- return ExtractTokenFromStdio ()
111
+ // Stdio only supports offline tokens via environment variable
112
+ token , err := ExtractTokenFromStdio ()
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ return & TokenInfo {Token : token , TokenType : "offline" }, nil
117
+
66
118
case "sse" :
67
- // For SSE transport, extract token from HTTP headers in the context
119
+ // For SSE transport, extract from HTTP headers
68
120
headers := extractHeadersFromContext (ctx )
69
121
if headers != nil {
70
- glog .V (2 ).Infof ("Headers found in context for SSE transport" )
71
- if token , err := ExtractTokenFromSSE (headers ); err == nil {
72
- return token , nil
122
+ if tokenInfo , err := ExtractTokenInfoFromSSE (headers ); err == nil {
123
+ return tokenInfo , nil
73
124
} else {
74
125
glog .Warningf ("Failed to extract token from SSE headers: %v" , err )
75
126
}
76
- } else {
77
- glog .Warningf ("No headers found in context for SSE transport" )
78
127
}
79
128
80
- // Fallback to environment variable for MVP compatibility
129
+ // Fallback to environment variable
81
130
token := os .Getenv (StdioTokenEnv )
82
131
if token == "" {
83
- err := fmt .Errorf ("SSE transport requires %s header or %s environment variable" , SSETokenHeader , StdioTokenEnv )
132
+ err := fmt .Errorf ("SSE transport requires %s header or %s environment variable" ,
133
+ AuthorizationHeader , StdioTokenEnv )
84
134
glog .Errorf ("Authentication failed: %v" , err )
85
- return "" , err
135
+ return nil , err
86
136
}
87
137
glog .V (2 ).Infof ("Using fallback environment variable for SSE transport" )
88
- return token , nil
138
+ return & TokenInfo {Token : token , TokenType : "offline" }, nil
139
+
89
140
default :
90
141
err := fmt .Errorf ("unsupported transport mode: %s" , transport )
91
142
glog .Errorf ("Authentication failed: %v" , err )
92
- return "" , err
143
+ return nil , err
93
144
}
94
145
}
95
146
0 commit comments