@@ -38,6 +38,20 @@ const (
3838 federatedTokenAudience = "chainloop"
3939)
4040
41+ func chooseAudience (audiences []string ) string {
42+ // Priority matters for mixed-audience tokens: user/api token identity
43+ // should win over generic federated audience.
44+ for _ , candidate := range []string {apiTokenAudience , userAudience , federatedTokenAudience } {
45+ for _ , aud := range audiences {
46+ if aud == candidate {
47+ return candidate
48+ }
49+ }
50+ }
51+
52+ return ""
53+ }
54+
4155// Parse the token and return the type of token. At the moment in Chainloop we have 3 types of tokens:
4256// 1. User account token
4357// 2. API token
@@ -65,20 +79,28 @@ func Parse(token string) (*ParsedToken, error) {
6579 return nil , nil
6680 }
6781
68- // Supports both string and array formats per JWT RFC 7519
69- // Takes first array element when multiple audiences exist
70- var audience string
82+ // Supports both string and array formats per JWT RFC 7519.
83+ // For multi-audience tokens, prefer the most specific Chainloop auth audience
84+ // instead of taking the first element (order can vary by issuer/runtime).
85+ var audiences []string
7186 switch aud := claims ["aud" ].(type ) {
7287 case string :
73- audience = aud
88+ audiences = [] string { aud }
7489 case []interface {}:
75- if len (aud ) > 0 {
76- audience , _ = aud [0 ].(string )
90+ for _ , a := range aud {
91+ if s , ok := a .(string ); ok && s != "" {
92+ audiences = append (audiences , s )
93+ }
7794 }
7895 default :
7996 return nil , nil
8097 }
8198
99+ if len (audiences ) == 0 {
100+ return nil , nil
101+ }
102+
103+ audience := chooseAudience (audiences )
82104 if audience == "" {
83105 return nil , nil
84106 }
0 commit comments