13
13
// limitations under the License.
14
14
15
15
using System ;
16
- using System . Net ;
17
16
using System . Net . Http ;
18
17
using System . Threading ;
19
18
using System . Threading . Tasks ;
19
+ using FirebaseAdmin . Util ;
20
20
using Google . Apis . Auth . OAuth2 ;
21
21
using Google . Apis . Http ;
22
22
using Google . Apis . Json ;
23
- using Google . Apis . Util ;
24
23
25
24
namespace FirebaseAdmin . Auth
26
25
{
@@ -38,14 +37,21 @@ internal class IAMSigner : ISigner
38
37
"https://iam.googleapis.com/v1/projects/-/serviceAccounts/{0}:signBlob" ;
39
38
40
39
private const string MetadataServerUrl =
41
- "http://metadata/computeMetadata/v1/instance/service-accounts/default/email" ;
40
+ "http://metadata.google.internal /computeMetadata/v1/instance/service-accounts/default/email" ;
42
41
43
- private readonly ConfigurableHttpClient httpClient ;
42
+ private readonly ErrorHandlingHttpClient < FirebaseAuthException > httpClient ;
44
43
private readonly Lazy < Task < string > > keyId ;
45
44
46
45
public IAMSigner ( HttpClientFactory clientFactory , GoogleCredential credential )
47
46
{
48
- this . httpClient = clientFactory . CreateAuthorizedHttpClient ( credential ) ;
47
+ this . httpClient = new ErrorHandlingHttpClient < FirebaseAuthException > (
48
+ new ErrorHandlingHttpClientArgs < FirebaseAuthException > ( )
49
+ {
50
+ HttpClientFactory = clientFactory ,
51
+ ErrorResponseHandler = IAMSignerErrorHandler . Instance ,
52
+ RequestExceptionHandler = AuthErrorHandler . Instance ,
53
+ DeserializeExceptionHandler = AuthErrorHandler . Instance ,
54
+ } ) ;
49
55
this . keyId = new Lazy < Task < string > > (
50
56
async ( ) => await DiscoverServiceAccountIdAsync ( clientFactory )
51
57
. ConfigureAwait ( false ) , true ) ;
@@ -55,25 +61,21 @@ public async Task<byte[]> SignDataAsync(
55
61
byte [ ] data , CancellationToken cancellationToken = default ( CancellationToken ) )
56
62
{
57
63
var keyId = await this . GetKeyIdAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
58
- var url = string . Format ( SignBlobUrl , keyId ) ;
59
- var request = new SignBlobRequest
64
+ var body = new SignBlobRequest
60
65
{
61
66
BytesToSign = Convert . ToBase64String ( data ) ,
62
67
} ;
63
-
64
- try
65
- {
66
- var response = await this . httpClient . PostJsonAsync ( url , request , cancellationToken )
67
- . ConfigureAwait ( false ) ;
68
- var json = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
69
- this . ThrowIfError ( response , json ) ;
70
- var parsed = NewtonsoftJsonSerializer . Instance . Deserialize < SignBlobResponse > ( json ) ;
71
- return Convert . FromBase64String ( parsed . Signature ) ;
72
- }
73
- catch ( HttpRequestException e )
68
+ var request = new HttpRequestMessage ( )
74
69
{
75
- throw new FirebaseException ( "Error while calling the IAM service." , e ) ;
76
- }
70
+ Method = HttpMethod . Post ,
71
+ RequestUri = new Uri ( string . Format ( SignBlobUrl , keyId ) ) ,
72
+ Content = NewtonsoftJsonSerializer . Instance . CreateJsonHttpContent ( body ) ,
73
+ } ;
74
+
75
+ var response = await this . httpClient
76
+ . SendAndDeserializeAsync < SignBlobResponse > ( request , cancellationToken )
77
+ . ConfigureAwait ( false ) ;
78
+ return Convert . FromBase64String ( response . Result . Signature ) ;
77
79
}
78
80
79
81
public virtual async Task < string > GetKeyIdAsync (
@@ -85,7 +87,8 @@ public virtual async Task<string> GetKeyIdAsync(
85
87
}
86
88
catch ( Exception e )
87
89
{
88
- throw new FirebaseException (
90
+ // Invalid configuration or environment error.
91
+ throw new InvalidOperationException (
89
92
"Failed to determine service account ID. Make sure to initialize the SDK "
90
93
+ "with service account credentials or specify a service account "
91
94
+ "ID with iam.serviceAccounts.signBlob permission. Please refer to "
@@ -105,38 +108,12 @@ private static async Task<string> DiscoverServiceAccountIdAsync(
105
108
using ( var client = clientFactory . CreateDefaultHttpClient ( ) )
106
109
{
107
110
client . DefaultRequestHeaders . Add ( "Metadata-Flavor" , "Google" ) ;
108
- return await client . GetStringAsync ( MetadataServerUrl ) . ConfigureAwait ( false ) ;
111
+ var resp = await client . GetAsync ( MetadataServerUrl ) . ConfigureAwait ( false ) ;
112
+ resp . EnsureSuccessStatusCode ( ) ;
113
+ return await resp . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
109
114
}
110
115
}
111
116
112
- private void ThrowIfError ( HttpResponseMessage response , string content )
113
- {
114
- if ( response . IsSuccessStatusCode )
115
- {
116
- return ;
117
- }
118
-
119
- string error = null ;
120
- try
121
- {
122
- var result = NewtonsoftJsonSerializer . Instance . Deserialize < SignBlobError > ( content ) ;
123
- error = result ? . Error . Message ;
124
- }
125
- catch ( Exception )
126
- {
127
- // Ignore any errors encountered while parsing the originl error.
128
- }
129
-
130
- if ( string . IsNullOrEmpty ( error ) )
131
- {
132
- error = "Response status code does not indicate success: "
133
- + $ "{ ( int ) response . StatusCode } ({ response . StatusCode } )"
134
- + $ "{ Environment . NewLine } { content } ";
135
- }
136
-
137
- throw new FirebaseException ( error ) ;
138
- }
139
-
140
117
/// <summary>
141
118
/// Represents the sign request sent to the remote IAM service.
142
119
/// </summary>
@@ -155,22 +132,16 @@ internal class SignBlobResponse
155
132
public string Signature { get ; set ; }
156
133
}
157
134
158
- /// <summary>
159
- /// Represents an error response sent by the remote IAM service.
160
- /// </summary>
161
- private class SignBlobError
135
+ private class IAMSignerErrorHandler : PlatformErrorHandler < FirebaseAuthException >
162
136
{
163
- [ Newtonsoft . Json . JsonProperty ( "error" ) ]
164
- public SignBlobErrorDetail Error { get ; set ; }
165
- }
137
+ internal static readonly IAMSignerErrorHandler Instance = new IAMSignerErrorHandler ( ) ;
166
138
167
- /// <summary>
168
- /// Represents the error details embedded in an IAM error response.
169
- /// </summary>
170
- private class SignBlobErrorDetail
171
- {
172
- [ Newtonsoft . Json . JsonProperty ( "message" ) ]
173
- public string Message { get ; set ; }
139
+ private IAMSignerErrorHandler ( ) { }
140
+
141
+ protected override FirebaseAuthException CreateException ( FirebaseExceptionArgs args )
142
+ {
143
+ return new FirebaseAuthException ( args . Code , args . Message , response : args . HttpResponse ) ;
144
+ }
174
145
}
175
146
}
176
147
}
0 commit comments