< Summary

Class:Azure.Identity.AadIdentityClient
Assembly:Azure.Identity
File(s):C:\Git\azure-sdk-for-net\sdk\identity\Azure.Identity\src\AadIdentityClient.cs
Covered lines:2
Uncovered lines:87
Coverable lines:89
Total lines:222
Line coverage:2.2% (2 of 89)
Covered branches:0
Total branches:14
Branch coverage:0% (0 of 14)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor()-100%100%
.ctor(...)-0%100%
AuthenticateAsync()-0%100%
Authenticate(...)-0%100%
AuthenticateAsync()-0%100%
Authenticate(...)-0%100%
SendAuthRequestAsync()-0%0%
SendAuthRequest(...)-0%0%
CreateClientSecretAuthRequest(...)-0%100%
CreateClientCertificateAuthRequest(...)-0%100%
CreateClientAssertionJWT(...)-0%100%
DeserializeAsync()-0%100%
Deserialize(...)-0%100%
Deserialize(...)-0%0%

File(s)

C:\Git\azure-sdk-for-net\sdk\identity\Azure.Identity\src\AadIdentityClient.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using Azure.Core;
 5using Azure.Core.Pipeline;
 6using System;
 7using System.IO;
 8using System.Security.Cryptography;
 9using System.Security.Cryptography.X509Certificates;
 10using System.Text;
 11using System.Text.Json;
 12using System.Threading;
 13using System.Threading.Tasks;
 14using Azure.Core.Diagnostics;
 15
 16namespace Azure.Identity
 17{
 18    internal class AadIdentityClient
 19    {
 20        private const string ClientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
 21
 22        private readonly CredentialPipeline _pipeline;
 23
 424        protected AadIdentityClient()
 25        {
 426        }
 27
 028        public AadIdentityClient(CredentialPipeline pipeline)
 29        {
 030            _pipeline = pipeline;
 031        }
 32
 33        public virtual async Task<AccessToken> AuthenticateAsync(string tenantId, string clientId, string clientSecret, 
 34        {
 035            using Request request = CreateClientSecretAuthRequest(tenantId, clientId, clientSecret, scopes);
 36
 037            return await SendAuthRequestAsync(request, cancellationToken).ConfigureAwait(false);
 038        }
 39
 40        public virtual AccessToken Authenticate(string tenantId, string clientId, string clientSecret, string[] scopes, 
 41        {
 042            using Request request = CreateClientSecretAuthRequest(tenantId, clientId, clientSecret, scopes);
 43
 044            return SendAuthRequest(request, cancellationToken);
 045        }
 46
 47        public virtual async Task<AccessToken> AuthenticateAsync(string tenantId, string clientId, X509Certificate2 clie
 48        {
 049            using Request request = CreateClientCertificateAuthRequest(tenantId, clientId, clientCertificate, scopes);
 50
 051            return await SendAuthRequestAsync(request, cancellationToken).ConfigureAwait(false);
 052        }
 53
 54        public virtual AccessToken Authenticate(string tenantId, string clientId, X509Certificate2 clientCertificate, st
 55        {
 056            using Request request = CreateClientCertificateAuthRequest(tenantId, clientId, clientCertificate, scopes);
 57
 058            return SendAuthRequest(request, cancellationToken);
 059        }
 60
 61        private async Task<AccessToken> SendAuthRequestAsync(Request request, CancellationToken cancellationToken)
 62        {
 063            Response response = await _pipeline.HttpPipeline.SendRequestAsync(request, cancellationToken).ConfigureAwait
 64
 065            if (response.Status == 200 || response.Status == 201)
 66            {
 067                AccessToken result = await DeserializeAsync(response.ContentStream, cancellationToken).ConfigureAwait(fa
 68
 069                return Response.FromValue(result, response);
 70            }
 71
 072            throw await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false);
 073        }
 74
 75        private AccessToken SendAuthRequest(Request request, CancellationToken cancellationToken)
 76        {
 077            Response response = _pipeline.HttpPipeline.SendRequest(request, cancellationToken);
 78
 079            if (response.Status == 200 || response.Status == 201)
 80            {
 081                AccessToken result = Deserialize(response.ContentStream);
 82
 083                return Response.FromValue(result, response);
 84            }
 85
 086            throw _pipeline.Diagnostics.CreateRequestFailedException(response);
 87        }
 88
 89        private Request CreateClientSecretAuthRequest(string tenantId, string clientId, string clientSecret, string[] sc
 90        {
 091            Request request = _pipeline.HttpPipeline.CreateRequest();
 92
 093            request.Method = RequestMethod.Post;
 94
 095            request.Headers.Add(HttpHeader.Common.FormUrlEncodedContentType);
 96
 097            request.Uri.Reset(_pipeline.AuthorityHost);
 98
 099            request.Uri.AppendPath(tenantId);
 100
 0101            request.Uri.AppendPath("/oauth2/v2.0/token", escape: false);
 102
 0103            var bodyStr = $"response_type=token&grant_type=client_credentials&client_id={Uri.EscapeDataString(clientId)}
 104
 0105            ReadOnlyMemory<byte> content = Encoding.UTF8.GetBytes(bodyStr).AsMemory();
 106
 0107            request.Content = RequestContent.Create(content);
 108
 0109            return request;
 110        }
 111
 112        private Request CreateClientCertificateAuthRequest(string tenantId, string clientId, X509Certificate2 clientCert
 113        {
 0114            Request request = _pipeline.HttpPipeline.CreateRequest();
 115
 0116            request.Method = RequestMethod.Post;
 117
 0118            request.Headers.Add(HttpHeader.Common.FormUrlEncodedContentType);
 119
 0120            request.Uri.Reset(_pipeline.AuthorityHost);
 121
 0122            request.Uri.AppendPath(tenantId);
 123
 0124            request.Uri.AppendPath("/oauth2/v2.0/token", escape: false);
 125
 0126            string clientAssertion = CreateClientAssertionJWT(clientId, request.Uri.ToString(), clientCertficate);
 127
 0128            var bodyStr = $"response_type=token&grant_type=client_credentials&client_id={Uri.EscapeDataString(clientId)}
 129
 0130            ReadOnlyMemory<byte> content = Encoding.UTF8.GetBytes(bodyStr).AsMemory();
 131
 0132            request.Content = RequestContent.Create(content);
 133
 0134            return request;
 135        }
 136
 137        private static string CreateClientAssertionJWT(string clientId, string audience, X509Certificate2 clientCertific
 138        {
 0139            var headerBuff = new ArrayBufferWriter<byte>();
 140
 0141            using (var headerJson = new Utf8JsonWriter(headerBuff))
 142            {
 0143                headerJson.WriteStartObject();
 144
 0145                headerJson.WriteString("typ", "JWT");
 0146                headerJson.WriteString("alg", "RS256");
 0147                headerJson.WriteString("x5t", Base64Url.HexToBase64Url(clientCertificate.Thumbprint));
 148
 0149                headerJson.WriteEndObject();
 150
 0151                headerJson.Flush();
 0152            }
 153
 0154            var payloadBuff = new ArrayBufferWriter<byte>();
 155
 0156            using (var payloadJson = new Utf8JsonWriter(payloadBuff))
 157            {
 0158                payloadJson.WriteStartObject();
 159
 0160                payloadJson.WriteString("jti", Guid.NewGuid());
 0161                payloadJson.WriteString("aud", audience);
 0162                payloadJson.WriteString("iss", clientId);
 0163                payloadJson.WriteString("sub", clientId);
 0164                payloadJson.WriteNumber("nbf", DateTimeOffset.UtcNow.ToUnixTimeSeconds());
 0165                payloadJson.WriteNumber("exp", (DateTimeOffset.UtcNow + TimeSpan.FromMinutes(30)).ToUnixTimeSeconds());
 166
 0167                payloadJson.WriteEndObject();
 168
 0169                payloadJson.Flush();
 0170            }
 171
 0172            string header = Base64Url.Encode(headerBuff.WrittenMemory.ToArray());
 173
 0174            string payload = Base64Url.Encode(payloadBuff.WrittenMemory.ToArray());
 175
 0176            string flattenedJws = header + "." + payload;
 177
 0178            byte[] signature = clientCertificate.GetRSAPrivateKey().SignData(Encoding.ASCII.GetBytes(flattenedJws), Hash
 179
 0180            return flattenedJws + "." + Base64Url.Encode(signature);
 181        }
 182
 183        private static async Task<AccessToken> DeserializeAsync(Stream content, CancellationToken cancellationToken)
 184        {
 0185            using (JsonDocument json = await JsonDocument.ParseAsync(content, default, cancellationToken).ConfigureAwait
 186            {
 0187                return Deserialize(json.RootElement);
 188            }
 0189        }
 190
 191        private static AccessToken Deserialize(Stream content)
 192        {
 0193            using (JsonDocument json = JsonDocument.Parse(content))
 194            {
 0195                return Deserialize(json.RootElement);
 196            }
 0197        }
 198
 199        private static AccessToken Deserialize(JsonElement json)
 200        {
 0201            string accessToken = null;
 202
 0203            DateTimeOffset expiresOn = DateTimeOffset.MaxValue;
 204
 0205            foreach (JsonProperty prop in json.EnumerateObject())
 206            {
 0207                switch (prop.Name)
 208                {
 209                    case "access_token":
 0210                        accessToken = prop.Value.GetString();
 0211                        break;
 212
 213                    case "expires_in":
 0214                        expiresOn = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(prop.Value.GetInt64());
 215                        break;
 216                }
 217            }
 218
 0219            return new AccessToken(accessToken, expiresOn);
 220        }
 221    }
 222}