/// <summary> /// Register a new SPA with SPA redirect URIs /// </summary> /// <param name="options">Command line options</param> /// <returns></returns> private async Task AddNewSpaWithSpaRedirectUri(Options options) { // We cannot the Graph SDK because spa is not available yet in the object // model of the Graph SDK string url = "https://graph.microsoft.com/v1.0/applications"; string body = Encoding.Default.GetString(Properties.Resources.spa_with_redirect_uri); body = body.Replace("[DisplayName]", options.AppName); body = body.Replace("[RedirectUri]", options.SpaRedirectUri); HttpClient httpClient = new HttpClient(); HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url); httpRequestMessage.Content = new StringContent(body, Encoding.UTF8, "application/json"); await authenticationProvider.AuthenticateRequestAsync(httpRequestMessage); HttpResponseMessage httpReponseMessage = await httpClient.SendAsync(httpRequestMessage); string responseContent = await httpReponseMessage.Content.ReadAsStringAsync(); string appId = null; if (httpReponseMessage.StatusCode == System.Net.HttpStatusCode.Created) { dynamic app = JsonConvert.DeserializeObject(responseContent); appId = app.appId; } string appUrl = $"https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/{appId}/isMSAApp/"; Console.WriteLine(appUrl); }
public async Task <SignInData> GetLatestUserLoginData(DateTime?fromDate = null) { string query = string.Empty; if (fromDate != null) { var fromDateFormatted = fromDate?.ToString("yyyy-MM-dd"); query = $"{_azureAdGraphSettings.BetaGraphApiBaseUrl}auditLogs/signIns?&$filter=createdDateTime ge {fromDateFormatted}"; } else { query = $"{_azureAdGraphSettings.BetaGraphApiBaseUrl}auditLogs/signIns"; } var message = new HttpRequestMessage() { Method = HttpMethod.Get, RequestUri = new Uri(query, UriKind.Absolute) }; await _authenticationProvider.AuthenticateRequestAsync(message); var response = await _graphServiceClient.HttpProvider.SendAsync(message); var responseJson = await response.Content.ReadAsStringAsync(); var deserializedSignInData = JsonConvert.DeserializeObject <SignInData>(responseJson); return(deserializedSignInData); }
public async Task ShouldUseDelegateAuthProviderWhenUserAccessTokenIsProvidedAsync() { // Arrange string accessToken = "ACCESS_TOKEN_VIA_DELEGATE_PROVIDER"; GraphSession.Instance.UserProvidedToken = new NetworkCredential(string.Empty, accessToken).SecurePassword; AuthContext userProvidedAuthContext = new AuthContext { AuthType = AuthenticationType.UserProvidedAccessToken, ContextScope = ContextScope.Process }; IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(userProvidedAuthContext); HttpRequestMessage requestMessage = new HttpRequestMessage(); // Act await authProvider.AuthenticateRequestAsync(requestMessage); // Assert Assert.IsType <DelegateAuthenticationProvider>(authProvider); Assert.Equal("Bearer", requestMessage.Headers.Authorization.Scheme); Assert.Equal(accessToken, requestMessage.Headers.Authorization.Parameter); // reset static instance. GraphSession.Reset(); }
/// <summary> /// Retry sending HTTP request /// </summary> /// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/>to send.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/>to send.</param> /// <param name="authProvider">An authentication provider to pass to <see cref="AuthenticationHandler"/> for authenticating requests.</param> /// <returns></returns> private async Task <HttpResponseMessage> SendRetryAsync(HttpResponseMessage httpResponseMessage, IAuthenticationProvider authProvider, CancellationToken cancellationToken) { int retryAttempt = 0; while (retryAttempt < MaxRetry) { // general clone request with internal CloneAsync (see CloneAsync for details) extension method var newRequest = await httpResponseMessage.RequestMessage.CloneAsync(); // Authenticate request using AuthenticationProvider await authProvider.AuthenticateRequestAsync(newRequest); httpResponseMessage = await base.SendAsync(newRequest, cancellationToken); retryAttempt++; if (!IsUnauthorized(httpResponseMessage) || !newRequest.IsBuffered()) { // Re-issue the request to get a new access token return(httpResponseMessage); } } return(httpResponseMessage); }
/// <summary> /// Retry sending HTTP request /// </summary> /// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/>to send.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/>to send.</param> /// <param name="authProvider">An authentication provider to pass to <see cref="AuthenticationHandler"/> for authenticating requests.</param> /// <returns></returns> private async Task <HttpResponseMessage> SendRetryAsync(HttpResponseMessage httpResponseMessage, IAuthenticationProvider authProvider, CancellationToken cancellationToken) { int retryAttempt = 0; while (retryAttempt < MaxRetry) { // general clone request with internal CloneAsync (see CloneAsync for details) extension method var newRequest = await httpResponseMessage.RequestMessage.CloneAsync(); // extract the www-authenticate header and add claims to the request context if (httpResponseMessage.Headers.WwwAuthenticate.Any()) { var wwwAuthenticateHeader = httpResponseMessage.Headers.WwwAuthenticate.ToString(); AddClaimsToRequestContext(newRequest, wwwAuthenticateHeader); } // Authenticate request using AuthenticationProvider await authProvider.AuthenticateRequestAsync(newRequest); httpResponseMessage = await base.SendAsync(newRequest, cancellationToken); retryAttempt++; if (!IsUnauthorized(httpResponseMessage) || !newRequest.IsBuffered()) { // Re-issue the request to get a new access token return(httpResponseMessage); } } return(httpResponseMessage); }
private async Task <string> HttpGetAsync(string relativeUrl) { var uri = serviceRoot + "/" + relativeUrl; if (relativeUrl.ToLower().IndexOf("https://") >= 0) { uri = relativeUrl; } var message = new HttpRequestMessage(HttpMethod.Get, uri); await authenticationProvider.AuthenticateRequestAsync(message); var client = new HttpClient(); var response = await client.SendAsync(message); response.EnsureSuccessStatusCode(); return(await response.Content.ReadAsStringAsync()); }
public Task AuthenticateRequestAsync(HttpRequestMessage request) { request.GetRequestContext().MiddlewareOptions[typeof(AuthenticationHandlerOption).ToString()] = new AuthenticationHandlerOption { AuthenticationProviderOption = authenticationProviderOption.Value }; return(innerProvider.AuthenticateRequestAsync(request)); }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { await _authenticationProvider.AuthenticateRequestAsync(request); return(await base.SendAsync(request, cancellationToken)); }
protected override void ProcessRecord() { base.ProcessRecord(); IAuthContext authContext = new AuthContext { TenantId = TenantId }; cancellationTokenSource = new CancellationTokenSource(); // Set selected environment to the session object. GraphSession.Instance.Environment = environment; switch (ParameterSetName) { case Constants.UserParameterSet: { // 2 mins timeout. 1 min < HTTP timeout. TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); cancellationTokenSource = new CancellationTokenSource(authTimeout); authContext.AuthType = AuthenticationType.Delegated; string[] processedScopes = ProcessScopes(Scopes); authContext.Scopes = processedScopes.Length == 0 ? new string[] { "User.Read" } : processedScopes; // Default to CurrentUser but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.CurrentUser; } break; case Constants.AppParameterSet: { authContext.AuthType = AuthenticationType.AppOnly; authContext.ClientId = ClientId; authContext.CertificateThumbprint = CertificateThumbprint; authContext.CertificateName = CertificateName; // Default to Process but allow the customer to change this via `ContextScope` param. authContext.ContextScope = this.IsParameterBound(nameof(ContextScope)) ? ContextScope : ContextScope.Process; } break; case Constants.AccessTokenParameterSet: { authContext.AuthType = AuthenticationType.UserProvidedAccessToken; authContext.ContextScope = ContextScope.Process; // Store user provided access token to a session object. GraphSession.Instance.UserProvidedToken = new NetworkCredential(string.Empty, AccessToken).SecurePassword; } break; } CancellationToken cancellationToken = cancellationTokenSource.Token; try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); IClientApplicationBase clientApplication = null; if (ParameterSetName == Constants.UserParameterSet) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } else if (ParameterSetName == Constants.AppParameterSet) { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authContext.Scopes, ForceRefresh = ForceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); authProvider.AuthenticateRequestAsync(httpRequestMessage).GetAwaiter().GetResult(); IAccount account = null; if (clientApplication != null) { // Only get accounts when we are using MSAL to get an access token. IEnumerable <IAccount> accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); account = accounts.FirstOrDefault(); } DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); // Save auth context to session state. GraphSession.Instance.AuthContext = authContext; } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { // DeviceCodeTimeout throw new Exception(string.Format( CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } WriteObject("Welcome To Microsoft Graph!"); }
protected override void ProcessRecord() { base.ProcessRecord(); AuthConfig authConfig = new AuthConfig { TenantId = TenantId }; CancellationToken cancellationToken = CancellationToken.None; if (ParameterSetName == Constants.UserParameterSet) { // 2 mins timeout. 1 min < HTTP timeout. TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); CancellationTokenSource cts = new CancellationTokenSource(authTimeout); cancellationToken = cts.Token; authConfig.AuthType = AuthenticationType.Delegated; authConfig.Scopes = Scopes ?? new string[] { "User.Read" }; } else { authConfig.AuthType = AuthenticationType.AppOnly; authConfig.ClientId = ClientId; authConfig.CertificateThumbprint = CertificateThumbprint; authConfig.CertificateName = CertificateName; } try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authConfig); IClientApplicationBase clientApplication = null; if (ParameterSetName == Constants.UserParameterSet) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } else { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instanciating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authConfig.Scopes, ForceRefresh = ForceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); authProvider.AuthenticateRequestAsync(httpRequestMessage).GetAwaiter().GetResult(); var accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); var account = accounts.FirstOrDefault(); JwtPayload jwtPayload = JwtHelpers.DecodeToObject <JwtPayload>(httpRequestMessage.Headers.Authorization?.Parameter); authConfig.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; authConfig.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; authConfig.AppName = jwtPayload?.AppDisplayname; authConfig.Account = jwtPayload?.Upn ?? account?.Username; // Save auth config to session state. SessionState.PSVariable.Set(Constants.GraphAuthConfigId, authConfig); } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { throw new Exception($"Device code terminal timed-out after {Constants.MaxDeviceCodeTimeOut} seconds. Please try again."); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } WriteObject("Welcome To Microsoft Graph!"); }
public async Task GetProfileDataAsync(ProfileDataRequestContext context) { //TODO : No userinfo endpoint, so we have to parse the AAD object to claims. //TODO : Create custome attributes in AAD. if (context.RequestedClaimTypes.Any()) { if (context.Client.ClientId == "DotNetDevOps.Emails") { // context.ValidatedRequest. context.AddRequestedClaims(context.Subject.Claims); } if (options.Schemes.Contains(context.Subject.GetIdentityProvider())) { var str = await azureB2CUserService.GetUserByObjectIdAsync(context.Subject.GetSubjectId()); context.AddRequestedClaims(GetClaims(str.Value)); if (context.RequestedClaimTypes.Contains("role")) { var roles = await azureB2CUserService.GetUserRolesAsync(context.Subject.GetSubjectId()); context.AddRequestedClaims(roles.Value.Select(v => new Claim("role", v))); } } if (context.Subject.GetIdentityProvider() == options.AzureADScheme) { string userId = context.Subject.FindFirst("oid")?.Value; if (userId != null) { string requestUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/memberOf?$select=displayName"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); await authenticationProvider.AuthenticateRequestAsync(request); HttpResponseMessage response = await httpClientFactory.CreateClient().SendAsync(request); var json = JObject.Parse(await response.Content.ReadAsStringAsync()); var claims = new List <Claim>() { new Claim("oid", userId) }; foreach (var group in json["value"]) { claims.Add(new Claim("role", group["displayName"].ToString(), System.Security.Claims.ClaimValueTypes.String, "Graph")); } context.AddRequestedClaims(claims); } } } // context.AddRequestedClaims(context.Subject.FindAll(JwtClaimTypes.Role)); }
protected override void ProcessRecord() { base.ProcessRecord(); AuthConfig authConfig = new AuthConfig { TenantId = TenantId }; CancellationToken cancellationToken = CancellationToken.None; if (ParameterSetName == Constants.UserParameterSet) { // 2 mins timeout. 1 min < HTTP timeout. TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); CancellationTokenSource cts = new CancellationTokenSource(authTimeout); cancellationToken = cts.Token; authConfig.AuthType = AuthenticationType.Delegated; authConfig.Scopes = Scopes ?? new string[] { "User.Read" }; } else { authConfig.AuthType = AuthenticationType.AppOnly; authConfig.ClientId = ClientId; authConfig.CertificateThumbprint = CertificateThumbprint; authConfig.CertificateName = CertificateName; } // Save auth config to session state. SessionState.PSVariable.Set(Constants.GraphAuthConfigId, authConfig); try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authConfig); // Incremental scope consent without re-instanciating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authConfig.Scopes, ForceRefresh = ForceRefresh } } } }; HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); // Trigger consent. authProvider.AuthenticateRequestAsync(httpRequestMessage).GetAwaiter().GetResult(); } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { throw new Exception($"Device code terminal timed-out after {Constants.MaxDeviceCodeTimeOut} seconds. Please try again."); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } WriteObject("Welcome To Microsoft Graph!"); // WriteObject(File.ReadAllText(".\\Art\\WelcomeText.txt")); // WriteObject(File.ReadAllText(".\\Art\\GRaphText.txt")); }
async public static Task <HttpResponseMessage> SendWithRetriesAsync(HttpRequestMessage originalRequest, IAuthenticationProvider authProvider, CancellationToken ct, TimeSpan delaySend = default(TimeSpan)) { // caller may have requested a delay if (delaySend > TimeSpan.Zero) { await Task.Delay(delaySend, ct); Logger.WriteLine($"Resuming delayed request after {delaySend.TotalSeconds} seconds."); } int throttlingRetryAttempt = 0; int networkErrorAttempt = 0; HttpResponseMessage responseMessage; // in this loop we retry on any type of error. we count throttling errors and network errors separately // throttle errors are to be expected and retried up to N times. each retry has up to M retries due to low level error errors while (true) { TimeSpan retryDelay; var request = await CloneHttpRequestMessageAsync(originalRequest); // authenticate the request before sending await authProvider.AuthenticateRequestAsync(request); try { responseMessage = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, ct); } // low level networking errors or cancelation due to timeout (TaskCanceledException is thrown) catch (Exception ex) when(ex is HttpRequestException || ex is TaskCanceledException) { if (networkErrorAttempt++ < maxNetworkErrorRetries && !ct.IsCancellationRequested) { retryDelay = TimeSpan.FromSeconds(networkErrorRetryDelayInSec * networkErrorAttempt); Logger.WriteLine($"Network error detected. Will retry after {retryDelay.TotalSeconds}s. Retry attempt no {networkErrorAttempt}. Error: {ex.Message}"); // retry loop continue; } else { throw ex; } } // response has been received. if (responseMessage.IsSuccessStatusCode) { return(responseMessage); } //let's check if it is in one of the error states we can recover from if (ShouldRetryOnThrottling(responseMessage, throttlingRetryAttempt, out retryDelay)) { ++throttlingRetryAttempt; networkErrorAttempt = 0; Logger.WriteLine($"Service throttling detected. Will retry after {retryDelay.TotalSeconds}s. Retry attempt no {throttlingRetryAttempt}"); } else if (ShouldRetryOnErrorResponseOrNetworkFailure(responseMessage, networkErrorAttempt, out retryDelay)) { ++networkErrorAttempt; Logger.WriteLine($"Retryable error code: {responseMessage.StatusCode}. Will retry after {retryDelay.TotalSeconds}s. Retry attempt no {networkErrorAttempt}"); } else { // we should not retry, return the response as is return(responseMessage); } // if we made it here, we need to wait and retry the loop Logger.WriteLine($"Waiting {retryDelay.TotalSeconds} to retry network request."); await Task.Delay(retryDelay).ConfigureAwait(false); } }
/// <summary> /// Authenticates the client using the provided <see cref="IAuthContext"/>. /// </summary> /// <param name="authContext">The <see cref="IAuthContext"/> to authenticate.</param> /// <param name="forceRefresh">Whether or not to force refresh a token if one exists.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public static async Task <IAuthContext> AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken) { try { // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); IClientApplicationBase clientApplication = null; if (authContext.AuthType == AuthenticationType.Delegated) { clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; } if (authContext.AuthType == AuthenticationType.AppOnly) { clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; } // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. GraphRequestContext graphRequestContext = new GraphRequestContext(); graphRequestContext.CancellationToken = cancellationToken; graphRequestContext.MiddlewareOptions = new Dictionary <string, IMiddlewareOption> { { typeof(AuthenticationHandlerOption).ToString(), new AuthenticationHandlerOption { AuthenticationProviderOption = new AuthenticationProviderOption { Scopes = authContext.Scopes, ForceRefresh = forceRefresh } } } }; // Trigger consent. HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); await authProvider.AuthenticateRequestAsync(httpRequestMessage); IAccount account = null; if (clientApplication != null) { // Only get accounts when we are using MSAL to get an access token. IEnumerable <IAccount> accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); account = accounts.FirstOrDefault(); } JwtHelpers.DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); return(authContext); } catch (AuthenticationException authEx) { if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) { // DeviceCodeTimeout throw new Exception(string.Format( CultureInfo.CurrentCulture, ErrorConstants.Message.DeviceCodeTimeout, Constants.MaxDeviceCodeTimeOut)); } else { throw authEx.InnerException ?? authEx; } } catch (Exception ex) { throw ex.InnerException ?? ex; } }