public async Task IssueAccessToken_InvalidRequest_ReturnsBadRequest() { var accessTokenGeneratorMock = new Mock <IKeyGenerator>(); var tokenStoreMock = new Mock <ISecurityTokenStore>(); using (var server = TestServer.Create(app => { app.Use <OioIdwsAuthorizationServiceMiddleware>(app, new OioIdwsAuthorizationServiceOptions { AccessTokenIssuerPath = new PathString("/accesstoken/issue"), AccessTokenRetrievalPath = new PathString("/accesstoken"), IssuerAudiences = () => Task.FromResult(new IssuerAudiences[0]), KeyGenerator = accessTokenGeneratorMock.Object, SecurityTokenStore = tokenStoreMock.Object, }); })) { server.BaseAddress = new Uri("https://localhost/"); var response = await server.CreateRequest("/accesstoken/issue").PostAsync(); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var authHeader = response.Headers.WwwAuthenticate.Single(x => x.Scheme == "Holder-of-key"); var bearerParameters = HttpHeaderUtils.ParseOAuthSchemeParameter(authHeader.Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidRequest, bearerParameters["error"]); } }
public async Task IssueAccessTokenFromStsToken_WrongAudience_ReturnsError() { var clientCertificate = CertificateUtil.GetCertificate("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5"); var issuerAudienceses = new[] { new IssuerAudiences("d9f10c97aa647727adb64a349bb037c5c23c9a7a", "sts cert") .Audience(new Uri("https://wrongAudience")), }; await PerformValidationTestAsync(clientCertificate, issuerAudienceses, (options, response) => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Audience was not known", errors["error_description"]); return(Task.FromResult(0)); }); }
public async Task IssueAccessTokenFromStsToken_ModifiedUnencryptedSecurityToken_ReturnsError() { var clientCertificate = CertificateUtil.GetCertificate("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5"); var issuerAudienceses = new[] { new IssuerAudiences("d9f10c97aa647727adb64a349bb037c5c23c9a7a", "sts cert") .Audience(new Uri("https://wsp.oioidws-net.dk")), }; var loggerMock = new Mock <ILogger>(); var loggerFactoryMock = new Mock <ILoggerFactory>(); loggerFactoryMock .Setup(x => x.Create("Digst.OioIdws.Rest.Server.AuthorizationServer.OioIdwsAuthorizationServiceMiddleware")) .Returns(() => loggerMock.Object); var decryptedSamlToken = GetDecryptedSamlToken(); var xml = XElement.Parse(decryptedSamlToken); var namespaceManager = new XmlNamespaceManager(new NameTable()); namespaceManager.AddNamespace("a", "urn:oasis:names:tc:SAML:2.0:assertion"); var elm = xml.XPathSelectElement("//a:Conditions", namespaceManager); var attr = elm.Attribute("NotOnOrAfter"); attr.Value = DateTime.UtcNow.AddHours(1).ToString("O"); await PerformValidationTestAsync(clientCertificate, issuerAudienceses, async (options, response) => { var str = await response.Content.ReadAsStringAsync(); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Token could not be parsed", errors["error_description"]); }, samlToken : () => xml.ToString(), setLogger : () => loggerFactoryMock.Object); loggerMock.Verify(v => v.WriteCore(TraceEventType.Error, 103, It.Is <object>(x => ((IDictionary <string, object>)x)["ValidationError"].ToString() == "Token could not be parsed"), It.IsAny <Exception>(), It.IsAny <Func <object, Exception, string> >())); }
public async Task IssueAccessTokenFromStsToken_ModifiedEncryptedSecurityToken_ReturnsError() { var clientCertificate = CertificateUtil.GetCertificate("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5"); var issuerAudienceses = new[] { new IssuerAudiences("d9f10c97aa647727adb64a349bb037c5c23c9a7a", "sts cert") .Audience(new Uri("https://wsp.oioidws-net.dk")), }; var loggerMock = new Mock <ILogger>(); var loggerFactoryMock = new Mock <ILoggerFactory>(); loggerFactoryMock .Setup(x => x.Create("Digst.OioIdws.Rest.Server.AuthorizationServer.OioIdwsAuthorizationServiceMiddleware")) .Returns(() => loggerMock.Object); var samlToken = GetSamlTokenXml(); var xml = XElement.Parse(samlToken); var namespaceManager = new XmlNamespaceManager(new NameTable()); namespaceManager.AddNamespace("e", "http://www.w3.org/2001/04/xmlenc#"); var elm = xml.XPathSelectElement("//e:EncryptedKey/e:CipherData/e:CipherValue", namespaceManager); elm.Value = "modified"; await PerformValidationTestAsync(clientCertificate, issuerAudienceses, (options, response) => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Token could not be parsed", errors["error_description"]); return(Task.FromResult(0)); }, samlToken : () => xml.ToString(), setLogger : () => loggerFactoryMock.Object); loggerMock.Verify(v => v.WriteCore(TraceEventType.Error, 103, It.Is <object>(x => ((IDictionary <string, object>)x)["ValidationError"].ToString() == "Token could not be parsed"), It.IsAny <Exception>(), It.IsAny <Func <object, Exception, string> >())); }
public async Task IssueAccessTokenFromStsToken_IncorrectClientCertificate_ReturnsError() { //wrong certificate var clientCertificate = CertificateUtil.GetCertificate("d9f10c97aa647727adb64a349bb037c5c23c9a7a"); var issuerAudienceses = new[] { new IssuerAudiences("d9f10c97aa647727adb64a349bb037c5c23c9a7a", "sts cert") .Audience(new Uri("https://wsp.oioidws-net.dk")), }; await PerformValidationTestAsync(clientCertificate, issuerAudienceses, (options, response) => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("X509Certificate used as SubjectConfirmationData did not match the provided client certificate", errors["error_description"]); return(Task.FromResult(0)); }); }
public async Task IssueAccessTokenFromStsToken_WrongIssuingCertificate_ReturnsError() { var clientCertificate = CertificateUtil.GetCertificate("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5"); var issuerAudienceses = new[] { //wrong issuing cert new IssuerAudiences("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5", "sts cert") .Audience(new Uri("https://wsp.oioidws-net.dk")), }; await PerformValidationTestAsync(clientCertificate, issuerAudienceses, (options, response) => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Issuer certificate 'D9F10C97AA647727ADB64A349BB037C5C23C9A7A' was unknown", errors["error_description"]); return(Task.FromResult(0)); }); }
public async Task Authenticate_UnknownToken_UnauthorizedByService() { await ProcessAuthenticateAsync( "bearer", (context, next) => { //this service requires auth, therefore denies context.Response.StatusCode = context.Request.User == null ? 401 : 200; return(Task.FromResult(0)); }, response => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Token information could not be retrieved from the Authorization Server. The access token might be unknown or expired", errors["error_description"]); return(Task.FromResult(0)); }, tokenFunc : () => null); }
public async Task IssueAccessTokenFromStsToken_ExpiredSecurityToken_ReturnsError() { var clientCertificate = CertificateUtil.GetCertificate("0E6DBCC6EFAAFF72E3F3D824E536381B26DEECF5"); var issuerAudienceses = new[] { new IssuerAudiences("d9f10c97aa647727adb64a349bb037c5c23c9a7a", "sts cert") .Audience(new Uri("https://wsp.oioidws-net.dk")), }; var samlToken = File.ReadAllText(@"Resources\ExpiredSecurityToken.xml"); await PerformValidationTestAsync(clientCertificate, issuerAudienceses, (options, response) => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("The Token is expired", errors["error_description"]); return(Task.FromResult(0)); }, samlToken : () => samlToken); }
public async Task Authenticate_InsufficientScope_ReturnsRequiredScope() { await ProcessAuthenticateAsync( "bearer", (context, next) => { //this service requires auth, therefore denies if nu user var authProperties = new AuthenticationProperties(); authProperties.Dictionary[AuthenticationErrorCodes.InsufficentScope] = "scope1"; context.Authentication.Challenge(authProperties); return(Task.FromResult(0)); }, response => { Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InsufficentScope, errors["error"]); Assert.AreEqual("scope1", errors["scope"]); return(Task.FromResult(0)); }); }
public async Task Authenticate_WrongTokenType_UnauthorizedByService() { await ProcessAuthenticateAsync( "bearer", (context, next) => { //this service requires auth, therefore denies context.Response.StatusCode = context.Request.User == null ? 401 : 200; return(Task.FromResult(0)); }, response => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("Authentication scheme was not valid", errors["error_description"]); return(Task.FromResult(0)); }, tokenFunc : () => new OioIdwsToken { Type = AccessTokenType.HolderOfKey }); }
public async Task Authenticate_InvalidHolderOfKeyCertificate_UnauthorizedByService() { await ProcessAuthenticateAsync( "holder-of-key", (context, next) => { //this service requires auth, therefore denies context.Response.StatusCode = context.Request.User == null ? 401 : 200; return(Task.FromResult(0)); }, response => { Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var errors = HttpHeaderUtils.ParseOAuthSchemeParameter(response.Headers.WwwAuthenticate.First().Parameter); Assert.AreEqual(AuthenticationErrorCodes.InvalidToken, errors["error"]); Assert.AreEqual("A valid certificate must be presented when presenting a Holder-of-key token", errors["error_description"]); return(Task.FromResult(0)); }, tokenFunc : () => new OioIdwsToken { Type = AccessTokenType.HolderOfKey, CertificateThumbprint = "correct cert thumbprint" }, certificateFunc : () => CertificateUtil.GetCertificate("d9f10c97aa647727adb64a349bb037c5c23c9a7a")); }
private static bool IsInvalidToken(HttpResponseMessage response) { return(response.Headers.WwwAuthenticate.Any(x => AccessTokenTypeParser.FromString(x.Scheme).HasValue&& HttpHeaderUtils.ParseOAuthSchemeParameter(x.Parameter)["error"].Equals(AuthenticationErrorCodes.InvalidToken, StringComparison.OrdinalIgnoreCase))); }
public async Task <AccessToken> GetTokenAsync(GenericXmlSecurityToken securityToken, CancellationToken cancellationToken) { if (securityToken == null) { throw new ArgumentNullException(nameof(securityToken)); } string samlToken; using (var stream = new MemoryStream()) { using (var writer = XmlWriter.Create(stream)) { securityToken.TokenXml.WriteTo(writer); } samlToken = Convert.ToBase64String(stream.ToArray()); } var requestHandler = new WebRequestHandler { ClientCertificates = { _settings.ClientCertificate } }; var formFields = new Dictionary <string, string> { { "saml-token", samlToken } }; if (_settings.DesiredAccessTokenExpiry.HasValue) { formFields["should-expire-in"] = ((int)_settings.DesiredAccessTokenExpiry.Value.TotalSeconds).ToString(); } var client = new HttpClient(requestHandler); var response = await client.PostAsync( _settings.AccessTokenIssuerEndpoint, new FormUrlEncodedContent(formFields), cancellationToken); if (!response.IsSuccessStatusCode) { //just find the first valid authenticate header we recognize var challenge = response.Headers.WwwAuthenticate .Select(x => new { Type = AccessTokenTypeParser.FromString(x.Scheme), x.Parameter }) .FirstOrDefault(x => x.Type.HasValue); if (challenge != null) { var parms = HttpHeaderUtils.ParseOAuthSchemeParameter(challenge.Parameter); throw new OioIdwsChallengeException( challenge.Type.Value, parms["error"], parms["error_description"], $@"Got unexpected challenge while issuing access token from '{ _settings.AccessTokenIssuerEndpoint }' ({response.StatusCode})': {parms["error"]} - {parms["error_description"]}"); } throw new InvalidOperationException( $@"Got unexpected response while issuing access token from '{_settings.AccessTokenIssuerEndpoint}' {response.StatusCode}: {await response.Content.ReadAsStringAsync()}"); } var json = await response.Content.ReadAsStringAsync(); var jsonValue = JObject.Parse(json); var accessToken = new Rest.Client.AccessToken.AccessToken { Value = (string)jsonValue["access_token"], ExpiresIn = TimeSpan.FromSeconds((int)jsonValue["expires_in"]), RetrievedAtUtc = DateTime.UtcNow, Type = ParseAccessTokenType((string)jsonValue["token_type"]) }; return(accessToken); }
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"]); } } }