Example #1
0
        public async Task when_refreshing_old_access_token_should_not_change_old_exp()
        {
            var tokenClient   = new TokenClient(TokenEndpoint, clientId, clientSecret, _handler);
            var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("bob", "bob", "api1 offline_access");

            var introspectionClient    = new IntrospectionClient(IntrospectionEndpoint, scope, scopeSecret, _handler);
            var introspectionResponse1 = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var exp1 = Int32.Parse(introspectionResponse1.Claims.Single(x => x.Item1 == "exp").Item2);

            await Task.Delay(1000);

            var refreshResponse = await tokenClient.RequestRefreshTokenAsync(tokenResponse.RefreshToken);

            var introspectionResponse2 = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var exp2 = Int32.Parse(introspectionResponse2.Claims.Single(x => x.Item1 == "exp").Item2);

            exp1.Should().Be(exp2);
        }
        private static async Task PrintClaimsIntrospectionClient()
        {
            // Get DiscoveryClient from IdentityServer using IdentityModel
            DiscoveryClient discoInstance = new DiscoveryClient(authority: authority)
            {
                Policy = new DiscoveryPolicy {
                    RequireHttps = false
                }                                                     // For development
            };

            DiscoveryResponse disco = await discoInstance.GetAsync();

            if (disco.IsError)
            {
                Console.WriteLine("Disco error {0}", disco.Error);
                return;
            }


            var introspectionClient = new IntrospectionClient(endpoint: disco.IntrospectionEndpoint, clientId: "api1", clientSecret: clientSecret);
            var response            = await introspectionClient.SendAsync(new IntrospectionRequest { Token = _token.AccessToken });

            if (!response.IsError)
            {
                Console.WriteLine("Claims for the user");
                response.Claims.ToList().ForEach(claim => Console.WriteLine("{0}: {1}", claim.Type, claim.Value));
                Console.WriteLine("\n\n");
            }
        }
Example #3
0
        public async Task Response_data_should_be_valid_using_multiple_scopes()
        {
            var tokenClient = new TokenClient(
                TokenEndpoint,
                "client1",
                "secret",
                _handler);

            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api3-a api3-b");

            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api3",
                "secret",
                _handler);

            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var values = response.Json.ToObject <Dictionary <string, object> >();

            values["aud"].GetType().Name.Should().Be("String");
            var iss      = values["iss"].GetType().Name.Should().Be("String");;
            var nbf      = values["nbf"].GetType().Name.Should().Be("Int64");;
            var exp      = values["exp"].GetType().Name.Should().Be("Int64");;
            var clientId = values["client_id"].GetType().Name.Should().Be("String");;
            var active   = values["active"].GetType().Name.Should().Be("Boolean");;
            var scopes   = (values["scope"] as JArray).Select(x => x.ToString()).ToArray();

            scopes.Length.Should().Be(2);
            scopes.Should().BeEquivalentTo(new string[] { "api3-a", "api3-b" });
        }
Example #4
0
        private static void TokenIntrospection(string introscpectionEndpoint, string accessToken, string scope, string secret)
        {
            var client = new IntrospectionClient(introscpectionEndpoint, scope, secret);

            var request = new IntrospectionRequest
            {
                Token = accessToken
            };

            var result = client.SendAsync(request).Result;

            if (result.IsError)
            {
                Console.WriteLine(result.Error);
            }
            else
            {
                if (result.IsActive)
                {
                    var claims          = result.Claims.ToList();
                    var expirationClaim = claims.FirstOrDefault(x => x.Type == "exp");
                    var validFromClaim  = claims.FirstOrDefault(x => x.Type == "nbf");

                    long expirationTimeStamp = Convert.ToInt64(expirationClaim.Value);
                    long validFromTimeStamp  = Convert.ToInt64(validFromClaim.Value);

                    var expirationDate = TimeStampToDate(expirationTimeStamp);
                    var issuedDate     = TimeStampToDate(validFromTimeStamp);
                }
                else
                {
                    Console.WriteLine("token is not active");
                }
            }
        }
        private static void Introspection(string accessToken)
        {
            var client = new IntrospectionClient(
                "https://localhost:44333/core/connect/introspect",
                "write",
                "secret");

            var request = new IntrospectionRequest
            {
                Token = accessToken
            };

            var result = client.SendAsync(request).Result;

            if (result.IsError)
            {
                Console.WriteLine(result.Error);
            }
            else
            {
                if (result.IsActive)
                {
                    result.Claims.ToList().ForEach(c => Console.WriteLine("{0}: {1}",
                                                                          c.Item1, c.Item2));
                }
                else
                {
                    Console.WriteLine("token is not active");
                }
            }
        }
Example #6
0
        public async Task Additional_request_parameters_should_be_handled_correctly()
        {
            var document = File.ReadAllText(FileName.Create("success_introspection_response.json"));
            var handler  = new NetworkHandler(document, HttpStatusCode.OK);

            var client = new IntrospectionClient(
                Endpoint,
                "client",
                innerHttpMessageHandler: handler);

            var additionalParams = new Dictionary <string, string>
            {
                { "scope", "scope1 scope2" },
                { "foo", "bar" }
            };

            var response = await client.SendAsync(new IntrospectionRequest { Token = "token", Parameters = additionalParams });

            // check request
            var fields = QueryHelpers.ParseQuery(handler.Body);

            fields.Count.Should().Be(4);

            fields["client_id"].First().Should().Be("client");
            fields["token"].First().Should().Be("token");
            fields["scope"].First().Should().Be("scope1 scope2");
            fields["foo"].First().Should().Be("bar");

            // check response
            response.IsError.Should().BeFalse();
            response.ErrorType.Should().Be(ResponseErrorType.None);
            response.HttpStatusCode.Should().Be(HttpStatusCode.OK);
            response.IsActive.Should().BeTrue();
            response.Claims.Should().NotBeEmpty();
        }
Example #7
0
        public async Task ShouldAuthenticateWhenUsingResourceOwnerFlowReferenceToken()
        {
            // arrange
            TokenResponse         tokenResponse;
            IntrospectionResponse introspectionResponse;
            var referenceScopeName      = fixture.Configuration["SpikeReferenceAudience"];
            var clientId                = fixture.Configuration["SpikeReferenceClient"];
            var username                = fixture.Configuration["SpikeTestUsername"];
            var password                = fixture.Configuration["SpikeTestPassword"];
            var scopeSecret             = fixture.Configuration["ScopeReferenceSecret"];
            var handler                 = fixture.Server.CreateHandler();
            DiscoveryResponse discovery = await GetDiscoveryResponse(handler);

            // act
            using (var tokenClient = new TokenClient(discovery.TokenEndpoint, clientId, secret, handler))
            {
                tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password, apiScope);
            }
            using (var introspectionClient = new IntrospectionClient(discovery.IntrospectionEndpoint, referenceScopeName, scopeSecret, handler))
            {
                introspectionResponse = await introspectionClient.SendAsync(new IntrospectionRequest()
                {
                    Token = tokenResponse.AccessToken
                });
            }
            // assert
            introspectionResponse.IsError.Should().BeFalse();
            introspectionResponse.Raw.Should().NotBeNullOrWhiteSpace();
        }
Example #8
0
        private static void Introspection(string accessToken)
        {
            var client = new IntrospectionClient(
                Constants.IntrospectionEndpoint,
                "api1",
                "secret");

            var request = new IntrospectionRequest
            {
                Token = accessToken
            };

            var result = client.SendAsync(request).Result;

            if (result.IsError)
            {
                Console.WriteLine(result.Error);
            }
            else
            {
                if (result.IsActive)
                {
                    result.Claims.ToList().ForEach(c => Console.WriteLine("{0}: {1}",
                                                                          c.Item1, c.Item2));
                }
                else
                {
                    Console.WriteLine("token is not active");
                }
            }
        }
Example #9
0
        public async Task Response_data_should_be_valid_using_multiple_scopes()
        {
            var tokenClient = new TokenClient(
                TokenEndpoint,
                "client1",
                "secret",
                _handler);

            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1 api2 unrestricted.api");

            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "unrestricted.api",
                "secret",
                _handler);


            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var values = response.Json.ToObject <Dictionary <string, object> >();

            values["aud"].GetType().Name.Should().Be("String");
            var iss      = values["iss"].GetType().Name.Should().Be("String");;
            var nbf      = values["nbf"].GetType().Name.Should().Be("Int64");;
            var exp      = values["exp"].GetType().Name.Should().Be("Int64");;
            var clientId = values["client_id"].GetType().Name.Should().Be("String");;
            var active   = values["active"].GetType().Name.Should().Be("Boolean");;
            var scopes   = values["scope"] as JArray;

            scopes.Count.Should().Be(3);
        }
Example #10
0
        public async Task Valid_Token_Valid_Scope_Multiple()
        {
            var tokenClient = new TokenClient(
                TokenEndpoint,
                "client1",
                "secret",
                _handler);

            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1 api2");

            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api1",
                "secret",
                _handler);

            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            response.IsActive.Should().Be(true);
            response.IsError.Should().Be(false);

            var scopes = from c in response.Claims
                         where c.Type == "scope"
                         select c;

            scopes.Count().Should().Be(1);
            scopes.First().Value.Should().Be("api1");
        }
        private static async Task <ClaimsPrincipal> ValidateToken(string jwtToken, string issuer, string resource)
        {
            var introspectionClient = new IntrospectionClient(
                Environment.GetEnvironmentVariable("IdpIntrospectionEndpoint"),
                Environment.GetEnvironmentVariable("IdpIntrospectionEndpointClientId"),
                Environment.GetEnvironmentVariable("IdpIntrospectionEndpointClientSecret"));

            var response = await introspectionClient.SendAsync(
                new IntrospectionRequest { Token = jwtToken });

            if (!response.IsActive)
            {
                return(null);
            }

            var handler = new JwtSecurityTokenHandler();

            handler.InboundClaimTypeMap.Clear();

            var principal = handler.ValidateToken(jwtToken, new TokenValidationParameters()
            {
                ValidIssuer              = issuer,
                ValidAudience            = resource,
                ValidateIssuerSigningKey = false,
                SignatureValidator       = (t, param) => new JwtSecurityToken(t),
                NameClaimType            = "sub"
            }, out SecurityToken _);

            return(principal);
        }
        public async Task <TokenValidationResult> IsValidToken(string accessToken)
        {
            var client = new IntrospectionClient(appSettings.IntrospectUrl);
            var result = await client.SendAsync(new IntrospectionRequest
            {
                Token = accessToken,

                ClientId     = appSettings.ClientId,
                ClientSecret = appSettings.ClientSecret
            }).ConfigureAwait(false);

            if (result.IsError)
            {
                Log.Error($"An error occurred while validating the access token - {result.Error}");
                return(TokenValidationResult.Error);
            }

            if (!result.IsActive)
            {
                Log.Error("Access token is not active");
                return(TokenValidationResult.Expired);
            }

            return(TokenValidationResult.Success);
        }
Example #13
0
        public async Task <JsonResult> IntroTestAsync()
        {
            var token  = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
            var client = "MainSite";
            var introspectionClient = new IntrospectionClient(DiscoveryResponse.IntrospectionEndpoint, client, Configuration["ApiInfo:Secrect"]);
            var response            = await introspectionClient.SendAsync(new IntrospectionRequest { Token = token });

            return(Json(""));
        }
Example #14
0
        public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            if (_options.EnableValidationResultCache)
            {
                var cachedClaims = await _options.ValidationResultCache.GetAsync(context.Token)
                                   .ConfigureAwait(false);

                if (cachedClaims != null)
                {
                    SetAuthenticationTicket(context, cachedClaims);
                    return;
                }
            }

            IntrospectionResponse response;

            try
            {
                response = await _client.SendAsync(new IntrospectionRequest { Token = context.Token })
                           .ConfigureAwait(false);

                if (response.IsError)
                {
                    _logger.WriteError("Error returned from introspection endpoint: " + response.Error);
                    return;
                }
                if (!response.IsActive)
                {
                    _logger.WriteVerbose("Inactive token: " + context.Token);
                    return;
                }
            }
            catch (Exception ex)
            {
                _logger.WriteError("Exception while contacting introspection endpoint: " + ex.ToString());
                return;
            }

            var claims = new List <Claim>();

            foreach (var claim in response.Claims)
            {
                if (!string.Equals(claim.Type, "active", StringComparison.Ordinal))
                {
                    claims.Add(new Claim(claim.Type, claim.Value));
                }
            }

            if (_options.EnableValidationResultCache)
            {
                await _options.ValidationResultCache.AddAsync(context.Token, claims)
                .ConfigureAwait(false);
            }

            SetAuthenticationTicket(context, claims);
        }
        async Task <bool> IsAccessTokenValidAsync(Tokens tokens)
        {
            var introspectionClient = new IntrospectionClient(MockIdSvrUiPipeline.IntrospectionEndpoint, scope_name, scope_secret, _mockPipeline.Handler);
            var response            = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token         = tokens.AccessToken,
                TokenTypeHint = IdentityModel.OidcConstants.TokenTypes.AccessToken
            });

            return(response.IsError == false && response.IsActive);
        }
Example #16
0
        public async Task introspecting_same_access_token_twice_should_have_same_exp()
        {
            var tokenClient   = new TokenClient(TokenEndpoint, clientId, clientSecret, _handler);
            var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("bob", "bob", "api1 offline_access");

            var introspectionClient    = new IntrospectionClient(IntrospectionEndpoint, scope, scopeSecret, _handler);
            var introspectionResponse1 = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var introspectionResponse2 = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var exp1 = Int32.Parse(introspectionResponse1.Claims.Single(x => x.Item1 == "exp").Item2);
            var exp2 = Int32.Parse(introspectionResponse2.Claims.Single(x => x.Item1 == "exp").Item2);

            exp1.Should().Be(exp2);
        }
Example #17
0
        public async Task Revoking_reference_token_should_invalidate_token()
        {
            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api1",
                "secret",
                innerHttpMessageHandler: _handler);

            var tokenClient = new TokenClient(
                TokenEndpoint,
                "roclient.reference",
                "secret",
                innerHttpMessageHandler: _handler);

            var revocationClient = new TokenRevocationClient(
                RevocationEndpoint,
                "roclient.reference",
                "secret",
                innerHttpMessageHandler: _handler);

            // request acccess token
            var response = await tokenClient.RequestResourceOwnerPasswordAsync("bob", "bob", "api1");

            response.IsError.Should().BeFalse();

            // introspect - should be active
            var introspectionResponse = await introspectionClient.SendAsync(
                new IntrospectionRequest { Token = response.AccessToken });

            introspectionResponse.IsActive.Should().Be(true);

            // revoke access token
            var revocationResponse = await revocationClient.RevokeAccessTokenAsync(response.AccessToken);

            // introspect - should be inactive
            introspectionResponse = await introspectionClient.SendAsync(
                new IntrospectionRequest { Token = response.AccessToken });

            introspectionResponse.IsActive.Should().Be(false);
        }
    public async Task <IEnumerable <String> > Secure()
    {
        var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");

        var introspectionClient = new IntrospectionClient("https://localhost:44388/connect/introspect", "MyAPI", "TopSecret");

        var response = await introspectionClient.SendAsync(new IntrospectionRequest { Token = accessToken });

        var isActive = response.IsActive;
        var claims   = response.Claims;

        return(new[] { "secure1", "secure2", $"isActive: {isActive}", JsonConvert.SerializeObject(claims) });
    }
Example #19
0
        private async Task <IntrospectionResponse> ValidateToken(string token)
        {
            var clientInstroption = new IntrospectionClient(WebConfigurationManager.AppSettings["is3host"] + "/identity/core/connect/introspect",
                                                            "sampleAPI",
                                                            "dmsecret");

            var request = new IntrospectionRequest
            {
                Token = token
            };

            return(clientInstroption.SendAsync(request).Result);
        }
Example #20
0
        /// <summary>
        ///     Introspection of access token using Identity Server 4's Introspect Client to connect to the introspection endpoint.
        /// </summary>
        public static async Task <IntrospectionResponse> OfAccessToken(IntrospectionTokenModel itm)
        {
            var introspectionClient = new IntrospectionClient(
                itm.IntrospectionPath,
                itm.ScopeName,
                itm.ScopePassword
                );


            return(await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = itm.AccessToken
            }));
        }
Example #21
0
        protected override async Task <int> ExecuteCommandAsync()
        {
            int result = -1;

            try
            {
                var discoClient = await DiscoveryClient.GetAsync(Server.Value());

                if (discoClient.IsError)
                {
                    Logger.LogError($"{discoClient.ErrorType} : {discoClient.Error}");
                }
                else
                {
                    var client = new IntrospectionClient(discoClient.IntrospectionEndpoint, Client.Value(),
                                                         Secret.Value());

                    var request = new IntrospectionRequest
                    {
                        Token = Token.Value()
                    };

                    var introspectionResponse = await client.SendAsync(request);

                    if (introspectionResponse.IsError)
                    {
                        Logger.LogError(introspectionResponse.Error);
                    }
                    else
                    {
                        if (introspectionResponse.IsActive)
                        {
                            introspectionResponse.Claims.ToList().ForEach(
                                c => Logger.LogInformation($"{c.Type}: {c.Value}"));
                        }
                        else
                        {
                            Logger.LogInformation("token is not active");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new CommandException(ex.Message);
            }

            return(result);
        }
Example #22
0
        public async Task Invalid_Token()
        {
            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api1",
                "secret",
                _handler);

            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = "invalid"
            });

            response.IsActive.Should().Be(false);
            response.IsError.Should().Be(false);
        }
Example #23
0
        public async Task Http_error_should_be_handled_correctly()
        {
            var handler = new NetworkHandler(HttpStatusCode.NotFound, "not found");

            var client = new IntrospectionClient(
                Endpoint,
                "client",
                innerHttpMessageHandler: handler);

            var response = await client.SendAsync(new IntrospectionRequest { Token = "token" });

            response.IsError.Should().BeTrue();
            response.ErrorType.Should().Be(ResponseErrorType.Http);
            response.HttpStatusCode.Should().Be(HttpStatusCode.NotFound);
            response.Error.Should().Be("not found");
        }
Example #24
0
        public async Task Exception_should_be_handled_correctly()
        {
            var handler = new NetworkHandler(new Exception("exception"));

            var client = new IntrospectionClient(
                Endpoint,
                "client",
                innerHttpMessageHandler: handler);

            var response = await client.SendAsync(new IntrospectionRequest { Token = "token" });

            response.IsError.Should().BeTrue();
            response.ErrorType.Should().Be(ResponseErrorType.Exception);
            response.Error.Should().Be("exception");
            response.Exception.Should().NotBeNull();
        }
Example #25
0
        public async Task Malformed_response_document_should_be_handled_correctly()
        {
            var document = "invalid";
            var handler  = new NetworkHandler(document, HttpStatusCode.OK);

            var client = new IntrospectionClient(
                Endpoint,
                "client",
                innerHttpMessageHandler: handler);

            var response = await client.SendAsync(new IntrospectionRequest { Token = "token" });

            response.IsError.Should().BeTrue();
            response.ErrorType.Should().Be(ResponseErrorType.Exception);
            response.Raw.Should().Be("invalid");
            response.Exception.Should().NotBeNull();
        }
        public async Task Response_data_with_user_authentication_should_be_valid_using_single_scope()
        {
            var tokenClient = new TokenClient(
                TokenEndpoint,
                "ro.client",
                "secret",
                _handler);

            var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("bob", "bob", "api1");

            tokenResponse.IsError.Should().BeFalse();

            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api1",
                "secret",
                _handler);

            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var values = response.Json.ToObject <Dictionary <string, object> >();

            values["aud"].GetType().Name.Should().Be("JArray");

            var audiences = ((JArray)values["aud"]);

            foreach (var aud in audiences)
            {
                aud.Type.Should().Be(JTokenType.String);
            }

            values["iss"].GetType().Name.Should().Be("String");
            values["nbf"].GetType().Name.Should().Be("Int64");
            values["exp"].GetType().Name.Should().Be("Int64");
            values["auth_time"].GetType().Name.Should().Be("Int64");
            values["client_id"].GetType().Name.Should().Be("String");
            values["sub"].GetType().Name.Should().Be("String");
            values["active"].GetType().Name.Should().Be("Boolean");

            var scopes = values["scope"] as JArray;

            scopes.Count.Should().Be(1);
        }
 private async Task <bool> CheckToken(string token)
 {
     try
     {
         var introspectUrl       = ConfigurationManager.AppSettings["identityServerIntrospectUrl"];
         var apiName             = ConfigurationManager.AppSettings["identityServerAPIName"];
         var apiSecret           = ConfigurationManager.AppSettings["identityServerAPISecret"];
         var introspectionClient = new IntrospectionClient(introspectUrl, apiName, apiSecret);
         var response            = introspectionClient.SendAsync(new IntrospectionRequest {
             Token = token
         }).Result;
         return(response.IsActive);
     }
     catch (Exception)
     {
         return(false);
     }
 }
Example #28
0
        public async Task Valid_protocol_response_should_be_handled_correctly()
        {
            var document = File.ReadAllText(Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "documents", "success_introspection_response.json"));
            var handler  = new NetworkHandler(document, HttpStatusCode.OK);

            var client = new IntrospectionClient(
                Endpoint,
                "client",
                innerHttpMessageHandler: handler);

            var response = await client.SendAsync(new IntrospectionRequest { Token = "token" });

            response.IsError.Should().BeFalse();
            response.ErrorType.Should().Be(ResponseErrorType.None);
            response.HttpStatusCode.Should().Be(HttpStatusCode.OK);
            response.IsActive.Should().BeTrue();
            response.Claims.Should().NotBeEmpty();
        }
        public async Task CanUseApiResource()
        {
            // arrange
            var httpClient = new ApiResourcesHttpClient(this.Authority, this.Handler);
            var resource   = new ApiResource
            {
                Name      = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
                ApiSecret = "secret",
            };

            await httpClient.AddApiResourceAsync(resource).ConfigureAwait(false);

            // act
            var client   = new IntrospectionClient(this.Authority + "/connect/introspect", resource.Name, resource.ApiSecret);
            var response = await client.SendAsync(new IntrospectionRequest { Token = "invalid" }).ConfigureAwait(false);

            // assert
            response.IsError.Should().BeFalse();
        }
Example #30
0
        public async Task Response_data_should_be_valid_using_multiple_scopes()
        {
            var tokenClient = new TokenClient(
                TokenEndpoint,
                "client1",
                "secret",
                _handler);

            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api3-a api3-b");

            var introspectionClient = new IntrospectionClient(
                IntrospectionEndpoint,
                "api3",
                "secret",
                _handler);

            var response = await introspectionClient.SendAsync(new IntrospectionRequest
            {
                Token = tokenResponse.AccessToken
            });

            var values = response.Json.ToObject <Dictionary <string, object> >();

            values["aud"].GetType().Name.Should().Be("JArray");

            var audiences = ((JArray)values["aud"]);

            foreach (var aud in audiences)
            {
                aud.Type.Should().Be(JTokenType.String);
            }

            values["iss"].GetType().Name.Should().Be("String");
            values["nbf"].GetType().Name.Should().Be("Int64");
            values["exp"].GetType().Name.Should().Be("Int64");
            values["client_id"].GetType().Name.Should().Be("String");
            values["active"].GetType().Name.Should().Be("Boolean");
            values["scope"].GetType().Name.Should().Be("String");

            var scopes = values["scope"].ToString();

            scopes.Should().Be("api3-a api3-b");
        }