public async Task RetrieveToken_IsInCache_ReturnedFromCache() { var accessToken = "token1"; var token = new OioIdwsToken { CertificateThumbprint = "cert1", ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1), }; var cacheMock = new Mock <ITokenCache>(); cacheMock .Setup(x => x.RetrieveAsync(accessToken)) .ReturnsAsync(token); Func <HttpClient> clientFactory = () => new HttpClient(); var sut = new RestTokenProvider(clientFactory, cacheMock.Object); sut.Initialize(null, null, new Mock <ILogger>().Object); var tokenResult = await sut.RetrieveTokenAsync(accessToken); Assert.IsTrue(tokenResult.Success); Assert.AreEqual(token.CertificateThumbprint, tokenResult.Result.CertificateThumbprint); cacheMock.Verify(x => x.RetrieveAsync(accessToken)); }
public virtual Task <ClaimsIdentity> BuildIdentityAsync(OioIdwsToken token) { if (token == null) { throw new ArgumentNullException(nameof(token)); } return(Task.FromResult( new ClaimsIdentity( token.Claims.Select(c => new Claim(c.Type, c.Value, c.ValueType, c.Issuer)).ToList(), "OioIdws"))); }
public Task StoreAsync(string accessToken, OioIdwsToken token) { if (accessToken == null) { throw new ArgumentNullException(nameof(accessToken)); } if (token == null) { throw new ArgumentNullException(nameof(token)); } _cache.TryAdd(accessToken, token); return(Task.FromResult(0)); }
public async Task BuildFromSamlToken() { var token = new OioIdwsToken { Type = AccessTokenType.Bearer, Claims = new[] { new OioIdwsClaim { Type = "nametype1", Value = "name1", }, new OioIdwsClaim { Type = "roletype1", Value = "role1", }, new OioIdwsClaim { Type = "type1", Value = "value1", ValueType = "valutype1", Issuer = "issuer1", }, new OioIdwsClaim { Type = "type2", Value = "value2", ValueType = "valutype2", Issuer = "issuer2", } }, }; var sut = new IdentityBuilder(); var identity = await sut.BuildIdentityAsync(token); Assert.IsNotNull(identity); Assert.IsTrue(identity.IsAuthenticated); Assert.AreEqual("OioIdws", identity.AuthenticationType); Assert.IsTrue(identity.HasClaim("nametype1", "name1")); Assert.AreEqual(4, identity.Claims.Count()); }
public async Task RetrieveToken_IsInCacheButExpired_ReturnedFromRestInvocation() { var accessToken = "token1"; var expiredToken = new OioIdwsToken { CertificateThumbprint = "cert1", ExpiresUtc = DateTimeOffset.UtcNow.AddHours(-1), }; var validToken = new OioIdwsToken { CertificateThumbprint = "cert2", ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1), }; var cacheMock = new Mock <ITokenCache>(); cacheMock .Setup(x => x.RetrieveAsync(accessToken)) .ReturnsAsync(expiredToken); var handler = new TokenHandler(validToken); Func <HttpClient> clientFactory = () => new HttpClient(handler) { BaseAddress = new Uri("http://dummy") }; var sut = new RestTokenProvider(clientFactory, cacheMock.Object); sut.Initialize(null, null, new Mock <ILogger>().Object); var tokenResult = await sut.RetrieveTokenAsync(accessToken); Assert.IsTrue(tokenResult.Success); Assert.AreEqual(validToken.CertificateThumbprint, tokenResult.Result.CertificateThumbprint); cacheMock.Verify(x => x.RetrieveAsync(accessToken)); }
public TokenHandler(OioIdwsToken token) { _token = token; }
public RetrieveTokenResult(OioIdwsToken result) { Result = result; }
public async Task IssueAsync(OioIdwsMatchEndpointContext context) { if (string.IsNullOrEmpty(context.Request.ContentType)) { SetInvalidRequest(context, "No content type was specified"); return; } var ct = new System.Net.Mime.ContentType(context.Request.ContentType); var validContentType = "application/x-www-form-urlencoded"; if (!ct.MediaType.Equals(validContentType, StringComparison.InvariantCultureIgnoreCase)) { SetInvalidRequest(context, $"Content type '{validContentType}' is required."); return; } var form = await context.Request.ReadFormAsync(); var tokenValueBase64 = form["saml-token"]; if (string.IsNullOrEmpty(tokenValueBase64)) { SetInvalidRequest(context, "saml-token was missing"); return; } string tokenValue; try { var bytes = Convert.FromBase64String(tokenValueBase64); using (var stream = new MemoryStream(bytes)) { using (var reader = new StreamReader(stream)) { tokenValue = await reader.ReadToEndAsync(); } } } catch (Exception) { SetInvalidRequest(context, "saml-token must be in base64"); return; } var clientCertificate = context.ClientCertificate(); _logger.WriteEntry(Log.StartingTokenValidation()); var samlTokenValidation = await _tokenValidator.ValidateTokenAsync(tokenValue, clientCertificate, context.Options); if (!samlTokenValidation.Success) { _logger.WriteEntry(Log.IssuingTokenDenied(samlTokenValidation.ErrorDescription, samlTokenValidation.ValidationException)); // Scheme is mandatory and Holder-Of-Key is currently the only supportede scheme at NemLog-in STS. Hence, we specify Holder-Of-Key. context.Response.SetAuthenticationFailed(AccessTokenType.HolderOfKey, AuthenticationErrorCodes.InvalidToken, samlTokenValidation.ErrorDescription); context.RequestCompleted(); return; } _logger.WriteEntry(Log.TokenValidationCompleted()); var expiresIn = context.Options.AccessTokenExpiration; int requestedExpiration; if (int.TryParse(form["should-expire-in"], out requestedExpiration)) { var tmp = TimeSpan.FromSeconds(requestedExpiration); if (tmp < expiresIn) { //if the client wants a lower expiration, that's ok. Never to increase it. expiresIn = tmp; } } var storedToken = new OioIdwsToken { CertificateThumbprint = samlTokenValidation.AccessTokenType == AccessTokenType.HolderOfKey ? clientCertificate?.Thumbprint?.ToLowerInvariant() : null, Type = samlTokenValidation.AccessTokenType, ExpiresUtc = context.Options.SystemClock.UtcNow + expiresIn, Claims = samlTokenValidation.ClaimsIdentity.Claims.Select(x => new OioIdwsClaim { Type = x.Type, Value = x.Value, Issuer = x.Issuer, ValueType = x.ValueType }).ToList(), }; var accessToken = await GenerateAccessTokenAsync(context, storedToken); await WriteAccessTokenAsync(context.Response, accessToken, samlTokenValidation.AccessTokenType, expiresIn); _logger.WriteEntry(Log.TokenIssuedWithExpiration(accessToken, expiresIn)); context.RequestCompleted(); }
private async Task <string> GenerateAccessTokenAsync(OioIdwsMatchEndpointContext context, OioIdwsToken storedToken) { var uniqueKey = _keyGenerator.GenerateUniqueKey(); _logger.WriteEntry(Log.NewUniqueKeyGenerated(uniqueKey)); await _securityTokenStore.StoreTokenAsync(uniqueKey, storedToken); _logger.WriteVerbose("Token information was committed to the Token Store"); //store the Expiry time directly in the protected access token, allowing the Authorization Server to quickly validate the token when asked to retrieve information var properties = new AuthenticationProperties { ExpiresUtc = storedToken.ExpiresUtc }; properties.Value(uniqueKey); var accessToken = context.Options.TokenDataFormat.Protect(properties); return(accessToken); }
public async Task RetrieveAccessToken_Success_TokenInformationIsInResponse() { var wspCertificate = CertificateUtil.GetCertificate("d9f10c97aa647727adb64a349bb037c5c23c9a7a"); var accessToken = "dummy"; var oioIdwsTokenKey = "accesstoken1"; var token = new OioIdwsToken { Type = AccessTokenType.Bearer, ExpiresUtc = DateTime.UtcNow.AddHours(1), Claims = new[] { new OioIdwsClaim { Type = "type1", Value = "value1", ValueType = "valuetype1", Issuer = "issuer1", }, new OioIdwsClaim { Type = "type2", Value = "value2", ValueType = "valuetype2", Issuer = "issuer2", }, } }; var tokenStoreMock = new Mock <ISecurityTokenStore>(); tokenStoreMock .Setup(x => x.RetrieveTokenAsync(oioIdwsTokenKey)) .ReturnsAsync(token); var tokenDataFormatMock = new Mock <ISecureDataFormat <AuthenticationProperties> >(); tokenDataFormatMock .Setup(x => x.Unprotect(accessToken)) .Returns(new AuthenticationProperties { ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1), Dictionary = { { "value", oioIdwsTokenKey } } }); var options = new OioIdwsAuthorizationServiceOptions { AccessTokenIssuerPath = new PathString("/accesstoken/issue"), AccessTokenRetrievalPath = new PathString("/accesstoken"), IssuerAudiences = () => Task.FromResult(new IssuerAudiences[0]), SecurityTokenStore = tokenStoreMock.Object, TokenDataFormat = tokenDataFormatMock.Object, TrustedWspCertificateThumbprints = new[] { "d9f10c97aa647727adb64a349bb037c5c23c9a7a" }, CertificateValidator = X509CertificateValidator.None //no reason for tests to validate certs }; using (var server = TestServerWithClientCertificate.Create(() => wspCertificate, app => { app.UseOioIdwsAuthorizationService(options); })) { server.BaseAddress = new Uri("https://localhost/"); var response = await server.HttpClient.GetAsync($"/accesstoken?{accessToken}"); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.AreEqual("application/json", response.Content.Headers.ContentType.MediaType); var responseToken = JsonConvert.DeserializeObject <OioIdwsToken>(await response.Content.ReadAsStringAsync()); Assert.AreEqual(token.Type, responseToken.Type); Assert.AreEqual(token.ExpiresUtc, responseToken.ExpiresUtc); Assert.AreEqual(token.Claims.Count, responseToken.Claims.Count); Assert.AreEqual(token.Claims.ElementAt(0).Type, responseToken.Claims.ElementAt(0).Type); Assert.AreEqual(token.Claims.ElementAt(0).Value, responseToken.Claims.ElementAt(0).Value); } }
public async Task RetrieveAccessToken_ExpiredAccessToken_ReturnsUnauthorized() { var wspCertificate = CertificateUtil.GetCertificate("d9f10c97aa647727adb64a349bb037c5c23c9a7a"); var accessToken = "accessToken1"; var oioIdwsTokenKey = "tokenValue1"; var tokenInformation = new OioIdwsToken(); var authProperties = new AuthenticationProperties { Dictionary = { { "value", oioIdwsTokenKey } } }; var tokenDataFormatMock = new Mock <ISecureDataFormat <AuthenticationProperties> >(); tokenDataFormatMock .Setup(x => x.Unprotect(accessToken)) .Returns(() => authProperties); var currentTime = DateTimeOffset.UtcNow; //ensure static time during test var timeMock = new Mock <ISystemClock>(); // ReSharper disable once AccessToModifiedClosure timeMock .SetupGet(x => x.UtcNow) .Returns(() => currentTime); var storeMock = new Mock <ISecurityTokenStore>(); storeMock .Setup(x => x.RetrieveTokenAsync(oioIdwsTokenKey)) .Returns(() => Task.FromResult(tokenInformation)); var options = new OioIdwsAuthorizationServiceOptions { AccessTokenIssuerPath = new PathString("/accesstoken/issue"), AccessTokenRetrievalPath = new PathString("/accesstoken"), IssuerAudiences = () => Task.FromResult(new IssuerAudiences[0]), TrustedWspCertificateThumbprints = new[] { "d9f10c97aa647727adb64a349bb037c5c23c9a7a" }, CertificateValidator = X509CertificateValidator.None, //no reason for tests to validate certs TokenDataFormat = tokenDataFormatMock.Object, SystemClock = timeMock.Object, MaxClockSkew = TimeSpan.FromMinutes(5), SecurityTokenStore = storeMock.Object, }; using (var server = TestServerWithClientCertificate.Create(() => wspCertificate, app => { app.UseOioIdwsAuthorizationService(options); })) { server.BaseAddress = new Uri("https://localhost/"); { //test that token content is checked properly authProperties.ExpiresUtc = currentTime - options.MaxClockSkew.Add(TimeSpan.FromSeconds(1)); var response = await server.HttpClient.GetAsync($"/accesstoken?{accessToken}"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var json = JObject.Parse(await response.Content.ReadAsStringAsync()); Assert.AreEqual(1, json["expired"].Value <int>()); } { //test that stored token information is checked properly authProperties.ExpiresUtc = currentTime; tokenInformation.ExpiresUtc = currentTime - options.MaxClockSkew.Add(TimeSpan.FromSeconds(1)); var response = await server.HttpClient.GetAsync($"/accesstoken?{accessToken}"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var json = JObject.Parse(await response.Content.ReadAsStringAsync()); Assert.AreEqual(1, json["expired"].Value <int>()); } } }
public async Task Authenticates_TokenExpired_IsUnauthorized() { var accessToken = "dummy"; var oioIdwsTokenKey = "token1"; var token = new OioIdwsToken { ExpiresUtc = DateTimeOffset.UtcNow.AddHours(-1), Claims = new[] { new OioIdwsClaim { Value = "hans", Type = "name" }, } }; var storeMock = new Mock <ISecurityTokenStore>(); storeMock .Setup(x => x.RetrieveTokenAsync(oioIdwsTokenKey)) .ReturnsAsync(token); var authProperties = new AuthenticationProperties { ExpiresUtc = DateTimeOffset.UtcNow.AddHours(-1), Dictionary = { { "value", oioIdwsTokenKey } } }; var tokenDataFormatMock = new Mock <ISecureDataFormat <AuthenticationProperties> >(); tokenDataFormatMock .Setup(x => x.Unprotect(accessToken)) .Returns(authProperties); using (var server = TestServer.Create(app => { app .UseOioIdwsAuthentication(new OioIdwsAuthenticationOptions()) .UseOioIdwsAuthorizationService(new OioIdwsAuthorizationServiceOptions { AccessTokenIssuerPath = new PathString("/accesstoken/issue"), IssuerAudiences = () => Task.FromResult(new[] { new IssuerAudiences("", "") }), SecurityTokenStore = storeMock.Object, TokenDataFormat = tokenDataFormatMock.Object, }) .Use((context, next) => { if (context.Request.Path == new PathString("/wsp")) { context.Response.StatusCode = context.Request.User != null ? 200 : 401; } return(Task.FromResult(0)); }); })) { var client = server.HttpClient; var authHeader = new AuthenticationHeaderValue("Bearer", accessToken); client.DefaultRequestHeaders.Authorization = authHeader; { var response = await client.GetAsync("/wsp"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Token was expired", errors["error_description"]); } { authProperties.ExpiresUtc = DateTimeOffset.Now.AddHours(1); var response = await client.GetAsync("/wsp"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Token was expired", errors["error_description"]); } } }
public async Task Authenticates_UseInMemoryTokenStore_IsAuthorized() { var accessToken = "dummy"; var oioIdwsTokenKey = "token1"; var token = new OioIdwsToken { ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1), Claims = new[] { new OioIdwsClaim { Value = "hans", Type = "name" }, } }; var storeMock = new Mock <ISecurityTokenStore>(); storeMock .Setup(x => x.RetrieveTokenAsync(oioIdwsTokenKey)) .ReturnsAsync(token); var tokenDataFormatMock = new Mock <ISecureDataFormat <AuthenticationProperties> >(); tokenDataFormatMock .Setup(x => x.Unprotect(accessToken)) .Returns(new AuthenticationProperties { ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1), Dictionary = { { "value", oioIdwsTokenKey } } }); using (var server = TestServer.Create(app => { app .UseOioIdwsAuthentication(new OioIdwsAuthenticationOptions()) .UseOioIdwsAuthorizationService(new OioIdwsAuthorizationServiceOptions { AccessTokenIssuerPath = new PathString("/accesstoken/issue"), IssuerAudiences = () => Task.FromResult(new [] { new IssuerAudiences("", "") }), SecurityTokenStore = storeMock.Object, TokenDataFormat = tokenDataFormatMock.Object, }) .Use((context, next) => { if (context.Request.Path == new PathString("/wsp")) { var name = ((ClaimsIdentity)context.Request.User.Identity).Claims.Single(x => x.Type == "name").Value; context.Response.Write(name); } return(Task.FromResult(0)); }); })) { var client = server.HttpClient; var authHeader = new AuthenticationHeaderValue("Bearer", accessToken); client.DefaultRequestHeaders.Authorization = authHeader; var response = await client.GetAsync("/wsp"); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var str = await response.Content.ReadAsStringAsync(); Assert.AreEqual("hans", str); } }