diff --git a/.gitignore b/.gitignore
index cffd936..8fc0f31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -153,3 +153,4 @@ distribution/
#Roslyn compiler temp folders
*.sln.ide/
+source/IdentityServer3.AccessTokenValidation.sln.GhostDoc.xml
diff --git a/README.md b/README.md
index 6c16606..30de880 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ You can either validate the tokens locally (JWTs only) or use the IdentityServer
```csharp
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
- Authority = "https://identityserver.io"
+ Authority = "https://identity.identityserver.io"
});
```
@@ -20,7 +20,7 @@ The middleware can also do the scope validation in one go.
```csharp
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
- Authority = "https://identityserver.io",
+ Authority = "https://identity.identityserver.io",
RequiredScopes = new[] { "api1", "api2" }
});
```
diff --git a/default.ps1 b/default.ps1
index 16ac260..5f84f0f 100644
--- a/default.ps1
+++ b/default.ps1
@@ -3,7 +3,7 @@ properties {
$src_directory = "$base_directory\source"
$output_directory = "$base_directory\build"
$dist_directory = "$base_directory\distribution"
- $sln_file = "$src_directory\Thinktecture.IdentityServer3.AccessTokenValidation.sln"
+ $sln_file = "$src_directory\IdentityServer3.AccessTokenValidation.sln"
$target_config = "Release"
$framework_version = "v4.5"
$xunit_path = "$src_directory\packages\xunit.runners.1.9.2\tools\xunit.console.clr4.exe"
@@ -11,7 +11,7 @@ properties {
$nuget_path = "$src_directory\.nuget\nuget.exe"
$buildNumber = 0;
- $version = "1.2.3.0"
+ $version = "2.0.0.0"
$preRelease = $null
}
@@ -53,19 +53,19 @@ task UpdateVersion {
}
task ILMerge -depends Compile {
- $input_dlls = "$output_directory\Thinktecture.IdentityServer.v3.AccessTokenValidation.dll"
+ $input_dlls = "$output_directory\IdentityServer.v3.AccessTokenValidation.dll"
Get-ChildItem -Path $output_directory -Filter *.dll |
foreach-object {
- # Exclude Thinktecture.IdentityServer.Core.dll as that will be the primary assembly
- if ("$_" -ne "Thinktecture.IdentityServer.v3.AccessTokenValidation.dll" -and
+ # Exclude IdentityServer3.AccessTokenValidation.dll as that will be the primary assembly
+ if ("$_" -ne "IdentityServer3.AccessTokenValidation.dll" -and
"$_" -ne "Owin.dll") {
$input_dlls = "$input_dlls $output_directory\$_"
}
}
New-Item $dist_directory\lib\net45 -Type Directory
- Invoke-Expression "$ilmerge_path /targetplatform:v4 /internalize:ilmerge.exclude /allowDup /target:library /out:$dist_directory\lib\net45\Thinktecture.IdentityServer.v3.AccessTokenValidation.dll $input_dlls"
+ Invoke-Expression "$ilmerge_path /targetplatform:v4 /internalize:ilmerge.exclude /allowDup /target:library /out:$dist_directory\lib\net45\IdentityServer.v3.AccessTokenValidation.dll $input_dlls"
}
task CreateNuGetPackage -depends Compile {
@@ -87,8 +87,8 @@ task CreateNuGetPackage -depends Compile {
}
New-Item $dist_directory\lib\net45 -Type Directory
- copy-item $output_directory\Thinktecture.IdentityServer3.AccessTokenValidation.* $dist_directory\lib\net45
+ copy-item $output_directory\IdentityServer3.AccessTokenValidation.* $dist_directory\lib\net45
- copy-item $src_directory\Thinktecture.IdentityServer3.AccessTokenValidation.nuspec $dist_directory
- exec { . $nuget_path pack $dist_directory\Thinktecture.IdentityServer3.AccessTokenValidation.nuspec -BasePath $dist_directory -o $dist_directory -version $packageVersion }
+ copy-item $src_directory\IdentityServer3.AccessTokenValidation.nuspec $dist_directory
+ exec { . $nuget_path pack $dist_directory\IdentityServer3.AccessTokenValidation.nuspec -BasePath $dist_directory -o $dist_directory -version $packageVersion }
}
diff --git a/mygetpush.cmd b/mygetpush.cmd
index abe28bc..6166665 100644
--- a/mygetpush.cmd
+++ b/mygetpush.cmd
@@ -1 +1 @@
-nuget push distribution\*.nupkg -Source https://www.myget.org/F/thinktecture/
+nuget push distribution\*.nupkg -Source https://www.myget.org/F/identity/
diff --git a/source/AccessTokenValidation.Tests/AccessTokenValidation.Tests.csproj b/source/AccessTokenValidation.Tests/AccessTokenValidation.Tests.csproj
index 317974b..ea6eadd 100644
--- a/source/AccessTokenValidation.Tests/AccessTokenValidation.Tests.csproj
+++ b/source/AccessTokenValidation.Tests/AccessTokenValidation.Tests.csproj
@@ -33,24 +33,33 @@
4
-
- False
- ..\packages\FluentAssertions.3.2.2\lib\net45\FluentAssertions.dll
+
+ ..\packages\FluentAssertions.3.4.1\lib\net45\FluentAssertions.dll
+ True
-
- False
- ..\packages\FluentAssertions.3.2.2\lib\net45\FluentAssertions.Core.dll
+
+ ..\packages\FluentAssertions.3.4.1\lib\net45\FluentAssertions.Core.dll
+ True
-
+
+ ..\packages\IdentityModel.1.0.0\lib\net45\IdentityModel.Net45.dll
+ True
+
+
False
- ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll
+ ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll
-
+
False
- ..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll
+ ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll
-
- ..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll
+
+ ..\packages\Moq.4.2.1507.0118\lib\net40\Moq.dll
+ True
+
+
+ False
+ ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
False
@@ -59,15 +68,12 @@
+
-
- False
- ..\packages\Thinktecture.IdentityModel.Core.1.3.0\lib\net45\Thinktecture.IdentityModel.Core.dll
-
..\packages\xunit.1.9.2\lib\net20\xunit.dll
@@ -77,6 +83,7 @@
+
diff --git a/source/AccessTokenValidation.Tests/InMemoryClaimsCacheTests.cs b/source/AccessTokenValidation.Tests/InMemoryClaimsCacheTests.cs
index 2d11ba8..0db91b2 100644
--- a/source/AccessTokenValidation.Tests/InMemoryClaimsCacheTests.cs
+++ b/source/AccessTokenValidation.Tests/InMemoryClaimsCacheTests.cs
@@ -1,9 +1,9 @@
-using Moq;
+using IdentityModel;
+using IdentityServer3.AccessTokenValidation;
+using Moq;
using System;
using System.Collections.Generic;
using System.Security.Claims;
-using Thinktecture.IdentityModel.Extensions;
-using Thinktecture.IdentityServer.AccessTokenValidation;
using Xunit;
namespace AccessTokenValidation.Tests
diff --git a/source/AccessTokenValidation.Tests/app.config b/source/AccessTokenValidation.Tests/app.config
new file mode 100644
index 0000000..8f9af70
--- /dev/null
+++ b/source/AccessTokenValidation.Tests/app.config
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/AccessTokenValidation.Tests/packages.config b/source/AccessTokenValidation.Tests/packages.config
index 4de2f7d..42d4f8d 100644
--- a/source/AccessTokenValidation.Tests/packages.config
+++ b/source/AccessTokenValidation.Tests/packages.config
@@ -1,11 +1,12 @@
-
-
-
-
+
+
+
+
+
+
-
\ No newline at end of file
diff --git a/source/AccessTokenValidation/AccessTokenValidation.csproj b/source/AccessTokenValidation/AccessTokenValidation.csproj
index 49a37df..9127837 100644
--- a/source/AccessTokenValidation/AccessTokenValidation.csproj
+++ b/source/AccessTokenValidation/AccessTokenValidation.csproj
@@ -7,8 +7,8 @@
{DF867B5D-3A9E-443A-B95E-D8F11E2A88A3}
Library
Properties
- Thinktecture.IdentityServer.AccessTokenValidation
- Thinktecture.IdentityServer3.AccessTokenValidation
+ IdentityServer3.AccessTokenValidation
+ IdentityServer3.AccessTokenValidation
v4.5
512
@@ -20,7 +20,7 @@
DEBUG;TRACE
prompt
4
- ..\..\build\Thinktecture.IdentityServer3.AccessTokenValidation.XML
+ ..\..\build\IdentityServer3.AccessTokenValidation.xml
pdbonly
@@ -29,43 +29,48 @@
TRACE
prompt
4
- ..\..\build\Thinktecture.IdentityServer3.AccessTokenValidation.XML
+ ..\..\build\IdentityServer3.AccessTokenValidation.xml
- False
- ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.1\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll
+ ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.0\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll
+ True
-
+
..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll
+ True
-
+
..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll
+ True
-
+
..\packages\Microsoft.Owin.Security.Jwt.3.0.0\lib\net45\Microsoft.Owin.Security.Jwt.dll
+ True
-
+
..\packages\Microsoft.Owin.Security.OAuth.3.0.0\lib\net45\Microsoft.Owin.Security.OAuth.dll
+ True
False
..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
-
+
..\packages\Owin.1.0\lib\net40\Owin.dll
+ True
- False
- ..\packages\System.IdentityModel.Tokens.Jwt.4.0.1\lib\net45\System.IdentityModel.Tokens.Jwt.dll
+ ..\packages\System.IdentityModel.Tokens.Jwt.4.0.0\lib\net45\System.IdentityModel.Tokens.Jwt.dll
+ True
-
+
False
- ..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll
+ ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll
@@ -76,27 +81,31 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
default.licenseheader
+
diff --git a/source/AccessTokenValidation/IdentityServerAccessTokenValidationAppBuilderExtensions.cs b/source/AccessTokenValidation/IdentityServerAccessTokenValidationAppBuilderExtensions.cs
deleted file mode 100644
index 4ea258d..0000000
--- a/source/AccessTokenValidation/IdentityServerAccessTokenValidationAppBuilderExtensions.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2015 Dominick Baier, Brock Allen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using Microsoft.Owin.Security.Jwt;
-using Microsoft.Owin.Security.OAuth;
-using System;
-using System.IdentityModel.Tokens;
-using System.Linq;
-using Thinktecture.IdentityServer.AccessTokenValidation;
-
-namespace Owin
-{
- ///
- /// Extension method for wiring up the access token validation middleware to the OWIN pipeline
- ///
- public static class IdentityServerAccessTokenValidationAppBuilderExtensions
- {
- ///
- /// Adds the access token validation middleware to the OWIN pipeline.
- ///
- /// The application.
- /// The options.
- ///
- /// options
- public static IAppBuilder UseIdentityServerBearerTokenAuthentication(this IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options)
- {
- if (options == null)
- {
- throw new ArgumentNullException("options");
- }
-
- if (options.ValidationMode == ValidationMode.Local)
- {
- app.UseLocalValidation(options);
- }
- else if (options.ValidationMode == ValidationMode.ValidationEndpoint)
- {
- app.UseValidationEndpoint(options);
- }
-
- if (options.RequiredScopes.Any())
- {
- app.Use(options.RequiredScopes);
- }
-
- return app;
- }
-
- internal static void UseLocalValidation(this IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options)
- {
- JwtFormat tokenFormat = null;
-
- // use discovery document to fully configure middleware
- if (!string.IsNullOrEmpty(options.Authority))
- {
- var discoveryEndpoint = options.Authority.EnsureTrailingSlash();
- discoveryEndpoint += ".well-known/openid-configuration";
-
- var issuerProvider = new CachingDiscoveryIssuerSecurityTokenProvider(
- discoveryEndpoint,
- options);
-
- if (options.TokenValidationParameters != null)
- {
- tokenFormat = new JwtFormat(options.TokenValidationParameters, issuerProvider);
- }
- else
- {
- var valParams = new TokenValidationParameters
- {
- ValidAudience = issuerProvider.Audience,
- NameClaimType = options.NameClaimType,
- RoleClaimType = options.RoleClaimType
- };
-
- tokenFormat = new JwtFormat(valParams, issuerProvider);
- }
- }
- // use token validation parameters
- else if (options.TokenValidationParameters != null)
- {
- tokenFormat = new JwtFormat(options.TokenValidationParameters);
- }
- // use simplified manual configuration
- else
- {
- var valParams = new TokenValidationParameters
- {
- ValidIssuer = options.IssuerName,
- ValidAudience = options.IssuerName.EnsureTrailingSlash() + "resources",
- IssuerSigningToken = new X509SecurityToken(options.IssuerCertificate),
- NameClaimType = options.NameClaimType,
- RoleClaimType = options.RoleClaimType
- };
-
- tokenFormat = new JwtFormat(valParams);
- }
-
- if (options.TokenHandler != null)
- {
- tokenFormat.TokenHandler = options.TokenHandler;
- }
-
- var bearerOptions = new OAuthBearerAuthenticationOptions
- {
- Provider = options.Provider,
- AccessTokenFormat = tokenFormat,
- AuthenticationMode = options.AuthenticationMode,
- AuthenticationType = options.AuthenticationType,
- Description = options.Description
- };
-
- app.UseOAuthBearerAuthentication(bearerOptions);
- }
-
- internal static void UseValidationEndpoint(this IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options)
- {
- if (options.EnableValidationResultCache)
- {
- if (options.ValidationResultCache == null)
- {
- options.ValidationResultCache = new InMemoryValidationResultCache(options);
- }
- }
-
- app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
- {
- AccessTokenProvider = new ValidationEndpointTokenProvider(options),
- Provider = options.Provider
- });
- }
- }
-}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/IdentityServerBearerTokensAuthenticationOptions.cs b/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs
similarity index 52%
rename from source/AccessTokenValidation/IdentityServerBearerTokensAuthenticationOptions.cs
rename to source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs
index a5fa250..f8f0f71 100644
--- a/source/AccessTokenValidation/IdentityServerBearerTokensAuthenticationOptions.cs
+++ b/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs
@@ -18,15 +18,13 @@
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
-using System.IdentityModel.Tokens;
using System.Linq;
using System.Net.Http;
-using System.Security.Cryptography.X509Certificates;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
- /// Options class for configuring the access token validation middleware
+ /// Configures identity server token validation
///
public class IdentityServerBearerTokenAuthenticationOptions : AuthenticationOptions
{
@@ -35,19 +33,24 @@ public class IdentityServerBearerTokenAuthenticationOptions : AuthenticationOpti
///
public IdentityServerBearerTokenAuthenticationOptions() : base("Bearer")
{
- ValidationMode = ValidationMode.ValidationEndpoint;
- RequiredScopes = Enumerable.Empty();
-
- ValidationResultCacheDuration = TimeSpan.FromMinutes(5);
-
NameClaimType = "name";
RoleClaimType = "role";
+
+ ValidationMode = ValidationMode.Both;
+ RequiredScopes = Enumerable.Empty();
+ ValidationResultCacheDuration = TimeSpan.FromMinutes(5);
}
- // common for local and validation endpoint
+ ///
+ /// Gets or sets the base address of identity server (required)
+ ///
+ ///
+ /// The authority.
+ ///
+ public string Authority { get; set; }
///
- /// Gets or sets the validation mode (either local for JWT tokens, or using the validation endpoint for both JWT and reference tokens.
+ /// Gets or sets the validation mode.
///
///
/// The validation mode.
@@ -55,20 +58,20 @@ public IdentityServerBearerTokenAuthenticationOptions() : base("Bearer")
public ValidationMode ValidationMode { get; set; }
///
- /// Gets or sets the base adress of IdentityServer - this is used to construct the URLs to the discovery document and the validation endpoint
+ /// Gets or sets the backchannel HTTP handler.
///
///
- /// The authority.
+ /// The backchannel HTTP handler.
///
- public string Authority { get; set; }
+ public WebRequestHandler BackchannelHttpHandler { get; set; }
///
- /// Gets or sets one of the required scopes to access the API
+ /// Gets or sets the backchannel certificate validator.
///
///
- /// The required scopes.
+ /// The backchannel certificate validator.
///
- public IEnumerable RequiredScopes { get; set; }
+ public ICertificateValidator BackchannelCertificateValidator { get; set; }
///
/// Gets or sets the type of the name claim.
@@ -87,78 +90,43 @@ public IdentityServerBearerTokenAuthenticationOptions() : base("Bearer")
public string RoleClaimType { get; set; }
///
- /// Gets or sets the name of the issuer (only use if authority is not set).
+ /// Gets or sets the token provider.
///
///
- /// The name of the issuer.
+ /// The token provider.
///
- public string IssuerName { get; set; }
+ public IOAuthBearerAuthenticationProvider TokenProvider { get; set; }
///
- /// Gets or sets the issuer certificate (only used if authority is not set).
+ /// Gets or sets the duration of the validation result cache.
///
///
- /// The issuer certificate.
+ /// The duration of the validation result cache.
///
- public X509Certificate2 IssuerCertificate { get; set; }
+ public TimeSpan ValidationResultCacheDuration { get; set; }
///
- /// Gets or sets a value indicating whether the result of the validation endpoint should be cached.
+ /// Gets or sets a value indicating whether to enable validation result caching.
///
///
- /// true if caching should be enabled; otherwise, false.
+ /// true if [enable validation result cache]; otherwise, false.
///
public bool EnableValidationResultCache { get; set; }
///
- /// Gets or sets the claims cache implementation (defaults to in-memory).
+ /// Gets or sets the validation result cache.
///
///
- /// The claims cache.
+ /// The validation result cache.
///
public IValidationResultCache ValidationResultCache { get; set; }
///
- /// Specifies for how long the validation results should be cached.
- ///
- ///
- /// The duration of the claims cache.
- ///
- public TimeSpan ValidationResultCacheDuration { get; set; }
-
- ///
- /// Gets or sets the authentication provider.
+ /// Gets or sets the required scopes.
///
///
- /// The provider.
- ///
- public IOAuthBearerAuthenticationProvider Provider { get; set; }
-
- ///
- /// Gets or sets the a certificate validator to use to validate the metadata endpoint.
- ///
- ///
- /// The certificate validator.
+ /// The required scopes.
///
- /// If this property is null then the default certificate checks are performed,
- /// validating the subject name and if the signing chain is a trusted party.
- public ICertificateValidator BackchannelCertificateValidator { get; set; }
-
- ///
- /// The HttpMessageHandler used to communicate with the metadata endpoint.
- /// This cannot be set at the same time as BackchannelCertificateValidator unless the value
- /// can be downcast to a WebRequestHandler.
- ///
- public HttpMessageHandler BackchannelHttpHandler { get; set; }
-
- ///
- /// Gets or sets the used to determine if a token is valid.
- ///
- public TokenValidationParameters TokenValidationParameters { get; set; }
-
- ///
- /// A System.IdentityModel.Tokens.SecurityTokenHandler designed for creating and validating Json Web Tokens.
- ///
- public JwtSecurityTokenHandler TokenHandler { get; set; }
+ public IEnumerable RequiredScopes { get; set; }
}
}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs b/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs
new file mode 100644
index 0000000..3bfa289
--- /dev/null
+++ b/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2015 Dominick Baier, Brock Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using IdentityServer3.AccessTokenValidation;
+using Microsoft.Owin.Logging;
+using Microsoft.Owin.Security.Jwt;
+using Microsoft.Owin.Security.OAuth;
+using System;
+using System.IdentityModel.Tokens;
+using System.Linq;
+
+namespace Owin
+{
+ ///
+ /// AppBuilder extensions for identity server token validation
+ ///
+ public static class IdentityServerBearerTokenValidationAppBuilderExtensions
+ {
+ ///
+ /// Add identity server token authentication to the pipeline.
+ ///
+ /// The application.
+ /// The options.
+ ///
+ public static IAppBuilder UseIdentityServerBearerTokenAuthentication(this IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options)
+ {
+ if (app == null) throw new ArgumentNullException("app");
+ if (options == null) throw new ArgumentNullException("options");
+ if (string.IsNullOrEmpty(options.Authority)) throw new ArgumentException("Authority must be set", "authority");
+
+ var loggerFactory = app.GetLoggerFactory();
+ var middlewareOptions = new IdentityServerOAuthBearerAuthenticationOptions();
+
+ if (options.ValidationMode == ValidationMode.Both ||
+ options.ValidationMode == ValidationMode.Local)
+ {
+ middlewareOptions.LocalValidationOptions = ConfigureLocalValidation(options, loggerFactory);
+ }
+
+ if (options.ValidationMode == ValidationMode.Both ||
+ options.ValidationMode == ValidationMode.ValidationEndpoint)
+ {
+ middlewareOptions.EndpointValidationOptions = ConfigureEndpointValidation(options, loggerFactory);
+ }
+
+ if (options.TokenProvider != null)
+ {
+ middlewareOptions.TokenProvider = options.TokenProvider;
+ }
+
+ app.Use(middlewareOptions);
+
+ if (options.RequiredScopes.Any())
+ {
+ app.Use(options.RequiredScopes);
+ }
+
+ return app;
+ }
+
+ private static OAuthBearerAuthenticationOptions ConfigureEndpointValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory)
+ {
+ if (options.EnableValidationResultCache)
+ {
+ if (options.ValidationResultCache == null)
+ {
+ options.ValidationResultCache = new InMemoryValidationResultCache(options);
+ }
+ }
+
+ var bearerOptions = new OAuthBearerAuthenticationOptions
+ {
+ AuthenticationMode = options.AuthenticationMode,
+ AuthenticationType = options.AuthenticationType,
+ AccessTokenProvider = new ValidationEndpointTokenProvider(options, loggerFactory),
+ Provider = new ContextTokenProvider(),
+ };
+
+ return bearerOptions;
+ }
+
+ internal static OAuthBearerAuthenticationOptions ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory)
+ {
+ var discoveryEndpoint = options.Authority.EnsureTrailingSlash();
+ discoveryEndpoint += ".well-known/openid-configuration";
+
+ var issuerProvider = new DiscoveryDocumentIssuerSecurityTokenProvider(
+ discoveryEndpoint,
+ options,
+ loggerFactory);
+
+ var valParams = new TokenValidationParameters
+ {
+ ValidAudience = issuerProvider.Audience,
+ NameClaimType = options.NameClaimType,
+ RoleClaimType = options.RoleClaimType
+ };
+
+ var tokenFormat = new JwtFormat(valParams, issuerProvider);
+
+ var bearerOptions = new OAuthBearerAuthenticationOptions
+ {
+ AccessTokenFormat = tokenFormat,
+ AuthenticationMode = options.AuthenticationMode,
+ AuthenticationType = options.AuthenticationType,
+ Provider = new ContextTokenProvider()
+ };
+
+ return bearerOptions;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs b/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs
new file mode 100644
index 0000000..006a4d2
--- /dev/null
+++ b/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 Dominick Baier, Brock Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Microsoft.Owin;
+using Microsoft.Owin.Builder;
+using Microsoft.Owin.Security.OAuth;
+using Owin;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AppFunc = System.Func, System.Threading.Tasks.Task>;
+
+namespace IdentityServer3.AccessTokenValidation
+{
+ ///
+ /// Middleware for validating identityserver access tokens
+ ///
+ public class IdentityServerBearerTokenValidationMiddleware
+ {
+ private readonly AppFunc _next;
+ private readonly AppFunc _localValidationFunc;
+ private readonly AppFunc _endpointValidationFunc;
+ private IdentityServerOAuthBearerAuthenticationOptions _options;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The next middleware.
+ /// The options.
+ public IdentityServerBearerTokenValidationMiddleware(AppFunc next, IdentityServerOAuthBearerAuthenticationOptions options)
+ {
+ _next = next;
+ _options = options;
+
+ if (options.LocalValidationOptions != null)
+ {
+ var localBuilder = new AppBuilder();
+ localBuilder.UseOAuthBearerAuthentication(options.LocalValidationOptions);
+ localBuilder.Run(ctx => next(ctx.Environment));
+ _localValidationFunc = localBuilder.Build();
+ }
+
+ if (options.EndpointValidationOptions != null)
+ {
+ var endpointBuilder = new AppBuilder();
+ endpointBuilder.Properties["host.AppName"] = "foobar";
+
+ endpointBuilder.UseOAuthBearerAuthentication(options.EndpointValidationOptions);
+ endpointBuilder.Run(ctx => next(ctx.Environment));
+ _endpointValidationFunc = endpointBuilder.Build();
+ }
+ }
+
+ ///
+ /// Invokes the middleware.
+ ///
+ /// The environment.
+ ///
+ public async Task Invoke(IDictionary environment)
+ {
+ var context = new OwinContext(environment);
+
+ var token = await GetTokenAsync(context);
+
+ if (token == null)
+ {
+ await _next(environment);
+ return;
+ }
+
+ context.Set("idsrv:tokenvalidation:token", token);
+
+
+ // seems to be a JWT
+ if (token.Contains('.'))
+ {
+ // see if local validation is setup
+ if (_localValidationFunc != null)
+ {
+ await _localValidationFunc(environment);
+ return;
+ }
+ // otherwise use validation endpoint
+ if (_endpointValidationFunc != null)
+ {
+ await _endpointValidationFunc(environment);
+ return;
+ }
+ }
+ else
+ {
+ // use validation endpoint
+ if (_endpointValidationFunc != null)
+ {
+ await _endpointValidationFunc(environment);
+ return;
+ }
+ }
+
+ await _next(environment);
+ }
+
+ private async Task GetTokenAsync(OwinContext context)
+ {
+ // find token in default location
+ string requestToken = null;
+ string authorization = context.Request.Headers.Get("Authorization");
+ if (!string.IsNullOrEmpty(authorization))
+ {
+ if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ requestToken = authorization.Substring("Bearer ".Length).Trim();
+ }
+ }
+
+ // give application opportunity to find from a different location, adjust, or reject token
+ if (_options.TokenProvider != null)
+ {
+ var requestTokenContext = new OAuthRequestTokenContext(context, requestToken);
+ await _options.TokenProvider.RequestToken(requestTokenContext);
+
+ // if no token found, no further work possible
+ if (string.IsNullOrEmpty(requestTokenContext.Token))
+ {
+ return null;
+ }
+
+ return requestTokenContext.Token;
+ }
+
+ return requestToken;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/IdentityServerOAuthBearerAuthenticationOptions.cs b/source/AccessTokenValidation/IdentityServerOAuthBearerAuthenticationOptions.cs
new file mode 100644
index 0000000..3d3c0f3
--- /dev/null
+++ b/source/AccessTokenValidation/IdentityServerOAuthBearerAuthenticationOptions.cs
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Dominick Baier, Brock Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Microsoft.Owin.Security.OAuth;
+
+namespace IdentityServer3.AccessTokenValidation
+{
+ ///
+ /// Options that wraps OAuth2BearerAuthenticationOptions for local and remote token validation
+ ///
+ public class IdentityServerOAuthBearerAuthenticationOptions
+ {
+ ///
+ /// Gets or sets the token provider (set this if the access token is NOT on the authorization header using a Bearer scheme.
+ ///
+ ///
+ /// The token provider.
+ ///
+ public IOAuthBearerAuthenticationProvider TokenProvider { get; set; }
+
+ ///
+ /// Gets or sets the local validation options.
+ ///
+ ///
+ /// The local validation options.
+ ///
+ public OAuthBearerAuthenticationOptions LocalValidationOptions { get; set; }
+
+ ///
+ /// Gets or sets the endpoint validation options.
+ ///
+ ///
+ /// The endpoint validation options.
+ ///
+ public OAuthBearerAuthenticationOptions EndpointValidationOptions { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/AsyncHelper.cs b/source/AccessTokenValidation/Plumbing/AsyncHelper.cs
similarity index 95%
rename from source/AccessTokenValidation/AsyncHelper.cs
rename to source/AccessTokenValidation/Plumbing/AsyncHelper.cs
index 67657c7..8b6bcb4 100644
--- a/source/AccessTokenValidation/AsyncHelper.cs
+++ b/source/AccessTokenValidation/Plumbing/AsyncHelper.cs
@@ -18,7 +18,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
internal static class AsyncHelper
{
diff --git a/source/AccessTokenValidation/Cache.cs b/source/AccessTokenValidation/Plumbing/Cache.cs
similarity index 92%
rename from source/AccessTokenValidation/Cache.cs
rename to source/AccessTokenValidation/Plumbing/Cache.cs
index 3c35dd7..339fcca 100644
--- a/source/AccessTokenValidation/Cache.cs
+++ b/source/AccessTokenValidation/Plumbing/Cache.cs
@@ -17,14 +17,14 @@
using System;
using System.Runtime.Caching;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// Cache implementation using System.Runtime.Cachine.MemoryCache
///
public class Cache : ICache
{
- const string CacheName = "thinktecture.validationCache";
+ const string CacheName = "IdentityServer3.validationCache";
readonly MemoryCache _cache = new MemoryCache(CacheName);
///
diff --git a/source/AccessTokenValidation/Clock.cs b/source/AccessTokenValidation/Plumbing/Clock.cs
similarity index 94%
rename from source/AccessTokenValidation/Clock.cs
rename to source/AccessTokenValidation/Plumbing/Clock.cs
index 457684c..5ffbd8f 100644
--- a/source/AccessTokenValidation/Clock.cs
+++ b/source/AccessTokenValidation/Plumbing/Clock.cs
@@ -16,7 +16,7 @@
using System;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// Default clock implementation based on DateTimeOffset
diff --git a/source/AccessTokenValidation/Plumbing/ContextTokenProvider.cs b/source/AccessTokenValidation/Plumbing/ContextTokenProvider.cs
new file mode 100644
index 0000000..d25a3b6
--- /dev/null
+++ b/source/AccessTokenValidation/Plumbing/ContextTokenProvider.cs
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 Dominick Baier, Brock Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Microsoft.Owin.Security.OAuth;
+using System;
+using System.Threading.Tasks;
+
+namespace IdentityServer3.AccessTokenValidation
+{
+ ///
+ /// Token provider that returns the token already found by the identityserver token middleware
+ ///
+ public class ContextTokenProvider : IOAuthBearerAuthenticationProvider
+ {
+ ///
+ /// Invoked before the is created. Gives the application an
+ /// opportunity to find the identity from a different location, adjust, or reject the token.
+ ///
+ /// Contains the token string.
+ ///
+ /// A representing the completed operation.
+ ///
+ public Task RequestToken(OAuthRequestTokenContext context)
+ {
+ context.Token = context.OwinContext.Get("idsrv:tokenvalidation:token");
+ return Task.FromResult(0);
+ }
+
+ ///
+ /// Called each time a challenge is being sent to the client. By implementing this method the application
+ /// may modify the challenge as needed.
+ ///
+ /// Contains the default challenge.
+ ///
+ /// A representing the completed operation.
+ ///
+ ///
+ public Task ApplyChallenge(OAuthChallengeContext context)
+ {
+ return Task.FromResult(0);
+ }
+
+ ///
+ /// Called each time a request identity has been validated by the middleware. By implementing this method the
+ /// application may alter or reject the identity which has arrived with the request.
+ ///
+ /// Contains information about the login session as well as the user .
+ ///
+ /// A representing the completed operation.
+ ///
+ public Task ValidateIdentity(OAuthValidateIdentityContext context)
+ {
+ return Task.FromResult(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/AccessTokenValidation/CachingDiscoveryIssuerSecurityTokenProvider.cs b/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs
similarity index 79%
rename from source/AccessTokenValidation/CachingDiscoveryIssuerSecurityTokenProvider.cs
rename to source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs
index c8962e6..35c62ac 100644
--- a/source/AccessTokenValidation/CachingDiscoveryIssuerSecurityTokenProvider.cs
+++ b/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs
@@ -15,6 +15,7 @@
*/
using Microsoft.IdentityModel.Protocols;
+using Microsoft.Owin.Logging;
using Microsoft.Owin.Security.Jwt;
using System;
using System.Collections.Generic;
@@ -24,20 +25,23 @@
using System.Security.Cryptography.X509Certificates;
using System.Threading;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
- internal class CachingDiscoveryIssuerSecurityTokenProvider : IIssuerSecurityTokenProvider
+ internal class DiscoveryDocumentIssuerSecurityTokenProvider : IIssuerSecurityTokenProvider
{
private readonly TimeSpan _refreshInterval = new TimeSpan(1, 0, 0, 0);
private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();
private readonly ConfigurationManager _configurationManager;
-
+ private readonly ILogger _logger;
+
private DateTimeOffset _syncAfter = new DateTimeOffset(new DateTime(2001, 1, 1));
private string _issuer;
private IEnumerable _tokens;
- public CachingDiscoveryIssuerSecurityTokenProvider(string discoveryEndpoint, IdentityServerBearerTokenAuthenticationOptions options)
+ public DiscoveryDocumentIssuerSecurityTokenProvider(string discoveryEndpoint, IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory)
{
+ _logger = loggerFactory.Create("IdentityServer3.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider");
+
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
if (options.BackchannelCertificateValidator != null)
@@ -133,13 +137,25 @@ private void RetrieveMetadata()
try
{
var result = AsyncHelper.RunSync(async () => await _configurationManager.GetConfigurationAsync());
+
+ if (result.JsonWebKeySet == null)
+ {
+ _logger.WriteError("Discovery document has no configured signing key. aborting.");
+ throw new InvalidOperationException("Discovery document has no configured signing key. aborting.");
+ }
+
var tokens = from key in result.JsonWebKeySet.Keys
- select new X509SecurityToken(new X509Certificate2(Convert.FromBase64String(key.X5c.First())));
+ select new X509SecurityToken(new X509Certificate2(Convert.FromBase64String(key.X5c.First())));
_issuer = result.Issuer;
_tokens = tokens;
_syncAfter = DateTimeOffset.UtcNow + _refreshInterval;
}
+ catch (Exception ex)
+ {
+ _logger.WriteError("Error contacting discovery endpoint: " + ex.ToString());
+ throw;
+ }
finally
{
_synclock.ExitWriteLock();
diff --git a/source/AccessTokenValidation/EpochTimeExtensions.cs b/source/AccessTokenValidation/Plumbing/EpochTimeExtensions.cs
similarity index 97%
rename from source/AccessTokenValidation/EpochTimeExtensions.cs
rename to source/AccessTokenValidation/Plumbing/EpochTimeExtensions.cs
index 09b4b84..936bee2 100644
--- a/source/AccessTokenValidation/EpochTimeExtensions.cs
+++ b/source/AccessTokenValidation/Plumbing/EpochTimeExtensions.cs
@@ -16,7 +16,7 @@
using System;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
internal static class EpochTimeExtensions
{
diff --git a/source/AccessTokenValidation/ICache.cs b/source/AccessTokenValidation/Plumbing/ICache.cs
similarity index 95%
rename from source/AccessTokenValidation/ICache.cs
rename to source/AccessTokenValidation/Plumbing/ICache.cs
index 8e90bbb..8b425d0 100644
--- a/source/AccessTokenValidation/ICache.cs
+++ b/source/AccessTokenValidation/Plumbing/ICache.cs
@@ -16,7 +16,7 @@
using System;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// Abstraction for a cache
diff --git a/source/AccessTokenValidation/IClock.cs b/source/AccessTokenValidation/Plumbing/IClock.cs
similarity index 93%
rename from source/AccessTokenValidation/IClock.cs
rename to source/AccessTokenValidation/Plumbing/IClock.cs
index 8a28f35..a1a3f74 100644
--- a/source/AccessTokenValidation/IClock.cs
+++ b/source/AccessTokenValidation/Plumbing/IClock.cs
@@ -16,7 +16,7 @@
using System;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// Interface to abstract the clock
diff --git a/source/AccessTokenValidation/IValidationResultCache.cs b/source/AccessTokenValidation/Plumbing/IValidationResultCache.cs
similarity index 95%
rename from source/AccessTokenValidation/IValidationResultCache.cs
rename to source/AccessTokenValidation/Plumbing/IValidationResultCache.cs
index d550406..7d6d23f 100644
--- a/source/AccessTokenValidation/IValidationResultCache.cs
+++ b/source/AccessTokenValidation/Plumbing/IValidationResultCache.cs
@@ -18,7 +18,7 @@
using System.Security.Claims;
using System.Threading.Tasks;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// Interface for caching then token validation result
diff --git a/source/AccessTokenValidation/InMemoryValidationResultCache.cs b/source/AccessTokenValidation/Plumbing/InMemoryValidationResultCache.cs
similarity index 65%
rename from source/AccessTokenValidation/InMemoryValidationResultCache.cs
rename to source/AccessTokenValidation/Plumbing/InMemoryValidationResultCache.cs
index 13d4019..6c34162 100644
--- a/source/AccessTokenValidation/InMemoryValidationResultCache.cs
+++ b/source/AccessTokenValidation/Plumbing/InMemoryValidationResultCache.cs
@@ -20,7 +20,7 @@
using System.Security.Claims;
using System.Threading.Tasks;
-namespace Thinktecture.IdentityServer.AccessTokenValidation
+namespace IdentityServer3.AccessTokenValidation
{
///
/// In-memory cache for validation results
@@ -29,15 +29,15 @@ public class InMemoryValidationResultCache : IValidationResultCache
{
private readonly IdentityServerBearerTokenAuthenticationOptions _options;
private readonly ICache _cache;
- private readonly IClock _clock;
+ private readonly IClock _clock;
///
/// Initializes a new instance of the class.
///
/// The options.
- public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOptions options)
+ public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOptions options)
: this(options, new Clock(), new Cache())
- { }
+ { }
///
/// Initializes a new instance of the class.
@@ -52,16 +52,16 @@ public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOpti
/// or
/// cache
///
- public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOptions options, IClock clock, ICache cache)
- {
- if (clock == null) { throw new ArgumentNullException("clock"); }
- if (options == null) { throw new ArgumentNullException("options"); }
- if (cache == null) { throw new ArgumentNullException("cache"); }
+ public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOptions options, IClock clock, ICache cache)
+ {
+ if (clock == null) { throw new ArgumentNullException("clock"); }
+ if (options == null) { throw new ArgumentNullException("options"); }
+ if (cache == null) { throw new ArgumentNullException("cache"); }
- _options = options;
- _cache = cache;
- _clock = clock;
- }
+ _options = options;
+ _cache = cache;
+ _clock = clock;
+ }
///
/// Add a validation result
@@ -69,25 +69,26 @@ public InMemoryValidationResultCache(IdentityServerBearerTokenAuthenticationOpti
/// The token.
/// The claims.
///
- public Task AddAsync(string token, IEnumerable claims)
+ public Task AddAsync(string token, IEnumerable claims)
{
- var expiryClaim = claims.FirstOrDefault(c => c.Type == ClaimTypes.Expiration);
- var cacheExpirySetting = _clock.UtcNow.Add(_options.ValidationResultCacheDuration);
-
- if (expiryClaim != null) {
- long epoch;
- if (long.TryParse(expiryClaim.Value, out epoch))
+ var expiryClaim = claims.FirstOrDefault(c => c.Type == ClaimTypes.Expiration);
+ var cacheExpirySetting = _clock.UtcNow.Add(_options.ValidationResultCacheDuration);
+
+ if (expiryClaim != null)
+ {
+ long epoch;
+ if (long.TryParse(expiryClaim.Value, out epoch))
{
- var tokenExpiresAt = epoch.ToDateTimeOffsetFromEpoch();
-
- if (tokenExpiresAt < cacheExpirySetting)
+ var tokenExpiresAt = epoch.ToDateTimeOffsetFromEpoch();
+
+ if (tokenExpiresAt < cacheExpirySetting)
{
- _cache.Add(token, claims, tokenExpiresAt);
- return Task.FromResult