@@ -2,6 +2,7 @@ use crate::config::Config;
2
2
use crate :: jwks;
3
3
use jsonwebkey as jwk;
4
4
use jsonwebtoken as jwt;
5
+ use jsonwebtoken:: { EncodingKey , Header } ;
5
6
use serde:: Serialize ;
6
7
use serde_json:: Value ;
7
8
use std:: collections:: HashMap ;
@@ -23,22 +24,95 @@ pub struct Maskinporten {
23
24
upstream_jwks : jwks:: Jwks ,
24
25
}
25
26
26
- #[ derive( Clone , Debug ) ]
27
- pub struct EntraID ( pub Config ) ;
27
+ #[ derive( Clone ) ]
28
+ pub struct AzureAD {
29
+ pub cfg : Config ,
30
+ private_jwk : jwt:: EncodingKey ,
31
+ client_assertion_header : jwt:: Header ,
32
+ upstream_jwks : jwks:: Jwks ,
33
+ }
28
34
29
35
#[ derive( Clone , Debug ) ]
30
36
pub struct TokenX ( pub Config ) ;
31
37
32
38
#[ derive( Serialize ) ]
33
- pub struct EntraIDTokenRequest { }
39
+ pub struct AzureADClientCredentialsTokenRequest {
40
+ grant_type : String , // client_credentials
41
+ client_id : String ,
42
+ client_assertion : String ,
43
+ client_assertion_type : String , // urn:ietf:params:oauth:client-assertion-type:jwt-bearer
44
+ scope : String ,
45
+ }
46
+
47
+ #[ derive( Serialize ) ]
48
+ pub struct AzureADOnBehalfOfTokenRequest {
49
+ grant_type : String , // urn:ietf:params:oauth:grant-type:jwt-bearer
50
+ client_id : String ,
51
+ client_assertion : String ,
52
+ client_assertion_type : String , // urn:ietf:params:oauth:client-assertion-type:jwt-bearer
53
+ scope : String ,
54
+ requested_token_use : String , // on_behalf_of
55
+ assertion : String ,
56
+ }
57
+
58
+ impl AzureAD {
59
+ pub fn on_behalf_of_request (
60
+ & self ,
61
+ target : String ,
62
+ user_token : String ,
63
+ ) -> AzureADOnBehalfOfTokenRequest {
64
+ let client_assertion = AssertionClaims :: new (
65
+ self . cfg . azure_ad_issuer . clone ( ) ,
66
+ self . cfg . azure_ad_client_id . clone ( ) ,
67
+ None ,
68
+ Some ( self . cfg . azure_ad_client_id . clone ( ) ) ,
69
+ )
70
+ . serialize ( & self . client_assertion_header , & self . private_jwk )
71
+ . unwrap ( ) ;
72
+
73
+ AzureADOnBehalfOfTokenRequest {
74
+ grant_type : "urn:ietf:params:oauth:grant-type:jwt-bearer" . to_string ( ) ,
75
+ client_id : self . cfg . azure_ad_client_id . clone ( ) ,
76
+ client_assertion,
77
+ client_assertion_type : "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
78
+ . to_string ( ) ,
79
+ scope : target,
80
+ requested_token_use : "on_behalf_of" . to_string ( ) ,
81
+ assertion : user_token,
82
+ }
83
+ }
84
+
85
+ pub fn new ( cfg : Config , upstream_jwks : jwks:: Jwks ) -> Self {
86
+ let client_private_jwk: jwk:: JsonWebKey = cfg. azure_ad_client_jwk . parse ( ) . unwrap ( ) ;
87
+ let alg: jwt:: Algorithm = client_private_jwk. algorithm . unwrap ( ) . into ( ) ;
88
+ let kid: String = client_private_jwk. key_id . clone ( ) . unwrap ( ) ;
89
+
90
+ let mut header = jwt:: Header :: new ( alg) ;
91
+ header. kid = Some ( kid) ;
92
+
93
+ Self {
94
+ cfg,
95
+ upstream_jwks,
96
+ private_jwk : client_private_jwk. key . to_encoding_key ( ) ,
97
+ client_assertion_header : header,
98
+ }
99
+ }
100
+ }
34
101
35
- impl Provider < EntraIDTokenRequest > for EntraID {
36
- fn token_request ( & self , _target : String ) -> EntraIDTokenRequest {
37
- EntraIDTokenRequest { }
102
+ impl Provider < AzureADClientCredentialsTokenRequest > for AzureAD {
103
+ fn token_request ( & self , _target : String ) -> AzureADClientCredentialsTokenRequest {
104
+ AzureADClientCredentialsTokenRequest {
105
+ grant_type : "client_credentials" . to_string ( ) ,
106
+ client_id : self . cfg . maskinporten_client_id . clone ( ) ,
107
+ client_assertion : "" . to_string ( ) ,
108
+ client_assertion_type : "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
109
+ . to_string ( ) ,
110
+ scope : "" . to_string ( ) ,
111
+ }
38
112
}
39
113
40
114
fn token_endpoint ( & self ) -> String {
41
- todo ! ( )
115
+ self . cfg . azure_ad_token_endpoint . to_string ( )
42
116
}
43
117
44
118
async fn introspect ( & mut self , _token : String ) -> HashMap < String , Value > {
@@ -71,22 +145,14 @@ pub struct MaskinportenTokenRequest {
71
145
72
146
impl Provider < MaskinportenTokenRequest > for Maskinporten {
73
147
fn token_request ( & self , target : String ) -> MaskinportenTokenRequest {
74
- let now = std:: time:: SystemTime :: now ( )
75
- . duration_since ( std:: time:: UNIX_EPOCH )
76
- . unwrap ( )
77
- . as_secs ( ) ;
78
- let jti = uuid:: Uuid :: new_v4 ( ) ;
79
-
80
- let claims = AssertionClaims {
81
- exp : ( now + 30 ) as usize ,
82
- iat : now as usize ,
83
- jti : jti. to_string ( ) ,
84
- scope : target. to_string ( ) ,
85
- iss : self . cfg . maskinporten_client_id . to_string ( ) ,
86
- aud : self . cfg . maskinporten_issuer . to_string ( ) ,
87
- } ;
88
-
89
- let token = jwt:: encode ( & self . client_assertion_header , & claims, & self . private_jwk ) . unwrap ( ) ;
148
+ let token = AssertionClaims :: new (
149
+ self . cfg . maskinporten_issuer . clone ( ) ,
150
+ self . cfg . maskinporten_client_id . clone ( ) ,
151
+ Some ( target) ,
152
+ None ,
153
+ )
154
+ . serialize ( & self . client_assertion_header , & self . private_jwk )
155
+ . unwrap ( ) ;
90
156
91
157
MaskinportenTokenRequest {
92
158
grant_type : "urn:ietf:params:oauth:grant-type:jwt-bearer" . to_string ( ) ,
@@ -137,8 +203,41 @@ impl Maskinporten {
137
203
struct AssertionClaims {
138
204
exp : usize ,
139
205
iat : usize ,
206
+ nbf : usize ,
140
207
jti : String ,
141
- scope : String ,
208
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
209
+ scope : Option < String > ,
142
210
iss : String ,
143
211
aud : String ,
212
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
213
+ sub : Option < String > ,
214
+ }
215
+
216
+ impl AssertionClaims {
217
+ fn new ( issuer : String , client_id : String , scope : Option < String > , sub : Option < String > ) -> Self {
218
+ let now = std:: time:: SystemTime :: now ( )
219
+ . duration_since ( std:: time:: UNIX_EPOCH )
220
+ . unwrap ( )
221
+ . as_secs ( ) ;
222
+ let jti = uuid:: Uuid :: new_v4 ( ) ;
223
+
224
+ AssertionClaims {
225
+ exp : ( now + 30 ) as usize ,
226
+ iat : now as usize ,
227
+ nbf : now as usize ,
228
+ jti : jti. to_string ( ) ,
229
+ scope,
230
+ sub,
231
+ iss : client_id, // issuer of the token is the client itself
232
+ aud : issuer, // audience of the token is the issuer
233
+ }
234
+ }
235
+
236
+ fn serialize (
237
+ & self ,
238
+ client_assertion_header : & Header ,
239
+ key : & EncodingKey ,
240
+ ) -> Result < String , jsonwebtoken:: errors:: Error > {
241
+ jwt:: encode ( client_assertion_header, & self , key)
242
+ }
144
243
}
0 commit comments