コード例 #1
0
    public async Task CachesExistingTokenWhenPossible()
    {
        // Arrange
        var tokenProvider = new Mock <IAccessTokenProvider>();

        tokenProvider.Setup(tp => tp.RequestAccessToken())
        .Returns(new ValueTask <AccessTokenResult>(new AccessTokenResult(AccessTokenResultStatus.Success,
                                                                         new AccessToken
        {
            Expires       = DateTime.Now.AddHours(1),
            GrantedScopes = new string[] { "All" },
            Value         = "asdf"
        },
                                                                         null,
                                                                         null)));

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());

        handler.ConfigureHandler(new[] { "https://localhost:5001" });

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        handler.InnerHandler = new TestMessageHandler(response);

        // Act
        _ = await new HttpClient(handler).GetAsync("https://localhost:5001/weather");
        response.RequestMessage = null;

        _ = await new HttpClient(handler).GetAsync("https://localhost:5001/weather");

        // Assert
        Assert.Single(tokenProvider.Invocations);
        Assert.Equal("asdf", response.RequestMessage.Headers.Authorization.Parameter);
    }
コード例 #2
0
    public async Task UsesCustomScopesAndReturnUrlWhenProvided()
    {
        // Arrange
        var tokenProvider = new Mock <IAccessTokenProvider>();

        tokenProvider.Setup(tp => tp.RequestAccessToken(It.IsAny <AccessTokenRequestOptions>()))
        .Returns(new ValueTask <AccessTokenResult>(new AccessTokenResult(AccessTokenResultStatus.Success,
                                                                         new AccessToken
        {
            Expires       = DateTime.Now.AddMinutes(3),
            GrantedScopes = new string[] { "All" },
            Value         = "asdf"
        },
                                                                         null,
                                                                         null)));

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());

        handler.ConfigureHandler(
            new[] { "https://localhost:5001" },
            scopes: new[] { "example.read", "example.write" },
            returnUrl: "https://www.example.com/return");

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        handler.InnerHandler = new TestMessageHandler(response);

        // Act
        _ = await new HttpClient(handler).GetAsync("https://localhost:5001/weather");

        // Assert
        Assert.Equal(1, tokenProvider.Invocations.Count);
    }
コード例 #3
0
    public async Task RequestNewTokenWhenCurrentTokenIsAboutToExpire()
    {
        // Arrange
        var tokenProvider = new Mock <IAccessTokenProvider>();

        tokenProvider.Setup(tp => tp.RequestAccessToken())
        .Returns(new ValueTask <AccessTokenResult>(new AccessTokenResult(AccessTokenResultStatus.Success,
                                                                         new AccessToken
        {
            Expires       = DateTime.Now.AddMinutes(3),
            GrantedScopes = new string[] { "All" },
            Value         = "asdf"
        },
                                                                         null,
                                                                         null)));

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());

        handler.ConfigureHandler(new[] { "https://localhost:5001" });

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        handler.InnerHandler = new TestMessageHandler(response);

        // Act
        _ = await new HttpClient(handler).GetAsync("https://localhost:5001/weather");
        response.RequestMessage = null;

        _ = await new HttpClient(handler).GetAsync("https://localhost:5001/weather");

        // Assert
        Assert.Equal(2, tokenProvider.Invocations.Count);
    }
コード例 #4
0
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);

            builder.RootComponents.Add <App>("app");

            builder.Services.AddTransient <AuthorizationMessageHandler>(sp =>
            {
                IAccessTokenProvider provider       = sp.GetRequiredService <IAccessTokenProvider>();
                NavigationManager naviManager       = sp.GetRequiredService <NavigationManager>();
                AuthorizationMessageHandler handler = new AuthorizationMessageHandler(provider, naviManager);
                handler.ConfigureHandler(authorizedUrls: new[] {
                    naviManager.ToAbsoluteUri("api/").AbsoluteUri
                });

                return(handler);
            });

            builder.Services.AddHttpClient("WebUI.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
            .AddHttpMessageHandler <AuthorizationMessageHandler>();

            builder.Services.AddTransient(sp =>
                                          new HttpClient
            {
                BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
            });

            builder.Services.AddApiAuthorization();
            await builder.Build().RunAsync();
        }
コード例 #5
0
    public async Task Throws_IfTheListOfAllowedUrlsIsNotConfigured()
    {
        // Arrange
        var expectedMessage = "The 'AuthorizationMessageHandler' is not configured. " +
                              "Call 'ConfigureHandler' and provide a list of endpoint urls to attach the token to.";

        var tokenProvider = new Mock <IAccessTokenProvider>();

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());
        // Act & Assert

        var exception = await Assert.ThrowsAsync <InvalidOperationException>(
            () => new HttpClient(handler).GetAsync("https://www.example.com"));

        Assert.Equal(expectedMessage, exception.Message);
    }
コード例 #6
0
    public async Task DoesNotAttachTokenToRequest_IfNotPresentInListOfAllowedUrls()
    {
        // Arrange
        var tokenProvider = new Mock <IAccessTokenProvider>();

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());

        handler.ConfigureHandler(new[] { "https://localhost:5001" });

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        handler.InnerHandler = new TestMessageHandler(response);

        // Act
        _ = await new HttpClient(handler).GetAsync("https://www.example.com");

        // Assert
        tokenProvider.VerifyNoOtherCalls();
    }
コード例 #7
0
    public async Task ThrowsWhenItCanNotProvisionANewToken()
    {
        // Arrange
        var tokenProvider = new Mock <IAccessTokenProvider>();

        tokenProvider.Setup(tp => tp.RequestAccessToken())
        .Returns(new ValueTask <AccessTokenResult>(new AccessTokenResult(AccessTokenResultStatus.RequiresRedirect,
                                                                         null,
                                                                         "https://www.example.com")));

        var handler = new AuthorizationMessageHandler(tokenProvider.Object, Mock.Of <NavigationManager>());

        handler.ConfigureHandler(new[] { "https://localhost:5001" });

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        handler.InnerHandler = new TestMessageHandler(response);

        // Act & assert
        var exception = await Assert.ThrowsAsync <AccessTokenNotAvailableException>(() => new HttpClient(handler).GetAsync("https://localhost:5001/weather"));
    }
コード例 #8
0
        /// <summary>
        /// This method gets called by the runtime.
        /// Use this method to add services to the container.
        /// </summary>
        ///
        /// <param name="builder">The builder.</param>
        public static void ConfigureBuilder(WebAssemblyHostBuilder builder)
        {
            #region [Required: Blazor Components]
            builder.RootComponents
            .Add <App>("app");

            builder.Services
            .AddOptions()
            .AddSharedLocalization <SharedResources>(options =>
            {
                options.DefaultCulture    = "en";
                options.SupportedCultures = new[] { "en", "pt" };
            });
            #endregion

            #region [Required: Blazor Authentication]
            builder.Services
            .AddOidcAuthentication(options =>
            {
                var settings = new MovieSettings();

                builder.Configuration.Bind(settings);

                // Authority
                options.ProviderOptions.Authority = settings.IdentityClientOptions.Authority;
                options.ProviderOptions.ClientId  = settings.IdentityClientOptions.ClientId;

                // Redirection
                options.ProviderOptions.RedirectUri           = settings.IdentityClientOptions.RedirectUri;
                options.ProviderOptions.PostLogoutRedirectUri = settings.IdentityClientOptions.PostLogoutRedirectUri;

                // Response
                options.ProviderOptions.ResponseMode = settings.IdentityClientOptions.ResponseMode;
                options.ProviderOptions.ResponseType = settings.IdentityClientOptions.ResponseType;

                // Scopes
                foreach (var scope in settings.IdentityClientOptions.Scopes)
                {
                    options.ProviderOptions.DefaultScopes.Add(scope);
                }
            });
            #endregion

            #region [Required: AutoMapper]
            builder.Services
            .AddAutoMapper(typeof(MovieMapperProfile).Assembly);
            #endregion

            #region [Required: Http Clients]
            builder.Services
            .AddHttpClient(HttpClientName, client =>
            {
                client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
            })
            .ConfigureHttpMessageHandlerBuilder(options =>
            {
                var settings = options.Services.GetService <IConfiguration>().Get <MovieSettings>();

                // Convert the enumerables to lists
                var blackListedUris = settings.IdentityClientOptions.BlackListedUris?.ToList() ?? new List <string>();
                var whiteListedUris = settings.IdentityClientOptions.WhiteListedUris?.ToList() ?? new List <string>();

                // Append the base address to the configured uris
                blackListedUris = blackListedUris
                                  .Select(uri => $"{builder.HostEnvironment.BaseAddress}{uri.ToLowerInvariant()}")
                                  .ToList();
                whiteListedUris = whiteListedUris
                                  .Select(uri => $"{builder.HostEnvironment.BaseAddress}{uri.ToLowerInvariant()}")
                                  .ToList();

                // Make sure there's at least one white-listed uri
                if (whiteListedUris.Count == 0)
                {
                    whiteListedUris.Add(builder.HostEnvironment.BaseAddress);
                }

                // Configure the handler
                var handler = new AuthorizationMessageHandler(options.Services);
                handler.ConfigureHandler(blackListedUris, whiteListedUris, settings.IdentityClientOptions.Scopes);

                // Assign the handler
                options.PrimaryHandler = handler;
            });

            builder.Services
            .AddTransient(provider =>
            {
                var factory = provider.GetRequiredService <IHttpClientFactory>();

                return(factory.CreateClient(HttpClientName));
            });
            #endregion

            #region [Required: Http Services]
            builder.Services
            .AddSingleton <IHttpService, HttpService>()
            .AddSingleton <IGenreService, GenreService>()
            .AddSingleton <IMovieService, MovieService>()
            .AddSingleton <IPersonService, PersonService>();
            #endregion

            #region [Required: FileReader]
            builder.Services
            .AddFileReaderService(options =>
            {
                options.InitializeOnFirstCall = true;
            });
            #endregion

            #region [Required: Toaster]
            builder.Services
            .AddToasterService();
            #endregion
        }