diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Auth/JwtAuthMessageHandler.cs b/DNN Platform/Dnn.AuthServices.Jwt/Auth/JwtAuthMessageHandler.cs
index 9cb16b9dc78..f729218712f 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Auth/JwtAuthMessageHandler.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Auth/JwtAuthMessageHandler.cs
@@ -2,74 +2,85 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Auth
-{
- using System;
- using System.Net.Http;
- using System.Security.Principal;
- using System.Threading;
+namespace Dnn.AuthServices.Jwt.Auth
+{
+ using System;
+ using System.Net.Http;
+ using System.Security.Principal;
+ using System.Threading;
- using Dnn.AuthServices.Jwt.Components.Common.Controllers;
- using DotNetNuke.Instrumentation;
- using DotNetNuke.Web.Api.Auth;
- using DotNetNuke.Web.ConfigSection;
+ using Dnn.AuthServices.Jwt.Components.Common.Controllers;
+ using DotNetNuke.Instrumentation;
+ using DotNetNuke.Web.Api.Auth;
+ using DotNetNuke.Web.ConfigSection;
- ///
- /// This class implements Json Web Token (JWT) authentication scheme.
- /// For detailed description of JWT refer to:
- /// - JTW standard https://tools.ietf.org/html/rfc7519.
- /// - Introduction to JSON Web Tokens http://jwt.io/introduction/.
- ///
- public class JwtAuthMessageHandler : AuthMessageHandlerBase
- {
- private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(JwtAuthMessageHandler));
-
- private readonly IJwtController _jwtController = JwtController.Instance;
-
- public JwtAuthMessageHandler(bool includeByDefault, bool forceSsl)
- : base(includeByDefault, forceSsl)
- {
- // Once an instance is enabled and gets registered in
- // ServicesRoutingManager.RegisterAuthenticationHandlers()
- // this scheme gets marked as enabled.
- IsEnabled = true;
- }
-
- public override string AuthScheme => this._jwtController.SchemeType;
-
- public override bool BypassAntiForgeryToken => true;
-
- internal static bool IsEnabled { get; set; }
-
- public override HttpResponseMessage OnInboundRequest(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- if (this.NeedsAuthentication(request))
- {
- this.TryToAuthenticate(request);
- }
-
- return base.OnInboundRequest(request, cancellationToken);
- }
-
- private void TryToAuthenticate(HttpRequestMessage request)
- {
- try
- {
- var username = this._jwtController.ValidateToken(request);
- if (!string.IsNullOrEmpty(username))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace($"Authenticated user '{username}'");
- }
-
- SetCurrentPrincipal(new GenericPrincipal(new GenericIdentity(username, this.AuthScheme), null), request);
- }
- }
- catch (Exception ex)
- {
- Logger.Error("Unexpected error in authenticating the user. " + ex);
- }
- }
- }
-}
+ ///
+ /// This class implements Json Web Token (JWT) authentication scheme.
+ /// For detailed description of JWT refer to:
+ /// - JTW standard https://tools.ietf.org/html/rfc7519.
+ /// - Introduction to JSON Web Tokens http://jwt.io/introduction/.
+ ///
+ public class JwtAuthMessageHandler : AuthMessageHandlerBase
+ {
+ private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(JwtAuthMessageHandler));
+
+ private readonly IJwtController jwtController = JwtController.Instance;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A value indicating whether this handler should be inlcuded by default on all API endpoints.
+ /// A value indicating whether this handler should enforce SSL usage.
+ public JwtAuthMessageHandler(bool includeByDefault, bool forceSsl)
+ : base(includeByDefault, forceSsl)
+ {
+ // Once an instance is enabled and gets registered in
+ // ServicesRoutingManager.RegisterAuthenticationHandlers()
+ // this scheme gets marked as enabled.
+ IsEnabled = true;
+ }
+
+ ///
+ public override string AuthScheme => this.jwtController.SchemeType;
+
+ ///
+ public override bool BypassAntiForgeryToken => true;
+
+ ///
+ /// Gets or sets a value indicating whether this handler is enabled.
+ ///
+ internal static bool IsEnabled { get; set; }
+
+ ///
+ public override HttpResponseMessage OnInboundRequest(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ if (this.NeedsAuthentication(request))
+ {
+ this.TryToAuthenticate(request);
+ }
+
+ return base.OnInboundRequest(request, cancellationToken);
+ }
+
+ private void TryToAuthenticate(HttpRequestMessage request)
+ {
+ try
+ {
+ var username = this.jwtController.ValidateToken(request);
+ if (!string.IsNullOrEmpty(username))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace($"Authenticated user '{username}'");
+ }
+
+ SetCurrentPrincipal(new GenericPrincipal(new GenericIdentity(username, this.AuthScheme), null), request);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error("Unexpected error in authenticating the user. " + ex);
+ }
+ }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/IJwtController.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/IJwtController.cs
index cb7dcefed46..9c99f797b55 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/IJwtController.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/IJwtController.cs
@@ -2,22 +2,50 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Common.Controllers
-{
- using System.Net.Http;
+namespace Dnn.AuthServices.Jwt.Components.Common.Controllers
+{
+ using System.Net.Http;
- using Dnn.AuthServices.Jwt.Components.Entity;
+ using Dnn.AuthServices.Jwt.Components.Entity;
- public interface IJwtController
- {
- string SchemeType { get; }
-
- string ValidateToken(HttpRequestMessage request);
-
- bool LogoutUser(HttpRequestMessage request);
-
- LoginResultData LoginUser(HttpRequestMessage request, LoginData loginData);
-
- LoginResultData RenewToken(HttpRequestMessage request, string renewalToken);
- }
-}
+ ///
+ /// Controls JWT features.
+ ///
+ public interface IJwtController
+ {
+ ///
+ /// Gets the name of the authentication Scheme Type.
+ ///
+ string SchemeType { get; }
+
+ ///
+ /// Validates the JWT token for the request.
+ ///
+ /// The current HTTP request.
+ /// Returns the UserName if the token is valid or null if not.
+ string ValidateToken(HttpRequestMessage request);
+
+ ///
+ /// Logs the user out.
+ ///
+ /// The current HTTP request.
+ /// A value indicating whether the logout attempt succeeded.
+ bool LogoutUser(HttpRequestMessage request);
+
+ ///
+ /// Logs the user in.
+ ///
+ /// The current HTTP request.
+ /// The login information, .
+ /// .
+ LoginResultData LoginUser(HttpRequestMessage request, LoginData loginData);
+
+ ///
+ /// Attempts to renew a JWT token.
+ ///
+ /// The current HTTP request.
+ /// The JWT renewal token.
+ /// .
+ LoginResultData RenewToken(HttpRequestMessage request, string renewalToken);
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/JwtController.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/JwtController.cs
index 52f03ba7ed4..30d81f039fe 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/JwtController.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Common/Controllers/JwtController.cs
@@ -2,558 +2,602 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Common.Controllers
-{
- using System;
- using System.Collections.Generic;
- using System.IdentityModel.Tokens;
- using System.Linq;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Security.Claims;
- using System.Security.Cryptography;
- using System.Text;
-
- using Dnn.AuthServices.Jwt.Auth;
- using Dnn.AuthServices.Jwt.Components.Entity;
- using Dnn.AuthServices.Jwt.Data;
- using DotNetNuke.Entities.Portals;
- using DotNetNuke.Entities.Users;
- using DotNetNuke.Framework;
- using DotNetNuke.Instrumentation;
- using DotNetNuke.Security.Membership;
- using DotNetNuke.Web.Api;
- using Newtonsoft.Json;
-
- internal class JwtController : ServiceLocator, IJwtController
- {
- public const string AuthScheme = "Bearer";
- public readonly IDataService DataProvider = DataService.Instance;
-
- private const int ClockSkew = 5; // in minutes; default for clock skew
- private const int SessionTokenTtl = 60; // in minutes = 1 hour
-
- private const int RenewalTokenTtl = 14; // in days = 2 weeks
- private const string SessionClaimType = "sid";
-
- private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(JwtController));
- private static readonly HashAlgorithm Hasher = SHA384.Create();
- private static readonly Encoding TextEncoder = Encoding.UTF8;
-
- public string SchemeType => "JWT";
-
- private static string NewSessionId => DateTime.UtcNow.Ticks.ToString("x16") + Guid.NewGuid().ToString("N").Substring(16);
-
- ///
- /// Validates the received JWT against the databas eand returns username when successful.
- ///
- ///
- public string ValidateToken(HttpRequestMessage request)
- {
- if (!JwtAuthMessageHandler.IsEnabled)
- {
- Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
- return null;
- }
-
- var authorization = this.ValidateAuthHeader(request?.Headers.Authorization);
- return string.IsNullOrEmpty(authorization) ? null : this.ValidateAuthorizationValue(authorization);
- }
-
- public bool LogoutUser(HttpRequestMessage request)
- {
- if (!JwtAuthMessageHandler.IsEnabled)
- {
- Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
- return false;
- }
-
- var rawToken = this.ValidateAuthHeader(request?.Headers.Authorization);
- if (string.IsNullOrEmpty(rawToken))
- {
- return false;
- }
-
- var jwt = new JwtSecurityToken(rawToken);
- var sessionId = GetJwtSessionValue(jwt);
- if (string.IsNullOrEmpty(sessionId))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Session ID not found in the claim");
- }
-
- return false;
- }
-
- this.DataProvider.DeleteToken(sessionId);
- return true;
- }
-
- ///
- /// Validates user login credentials and returns result when successful.
- ///
- ///
- public LoginResultData LoginUser(HttpRequestMessage request, LoginData loginData)
- {
- if (!JwtAuthMessageHandler.IsEnabled)
- {
- Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
- return EmptyWithError("disabled");
- }
-
- var portalSettings = PortalController.Instance.GetCurrentPortalSettings();
- if (portalSettings == null)
- {
- Logger.Trace("portalSettings = null");
- return EmptyWithError("no-portal");
- }
-
- var status = UserLoginStatus.LOGIN_FAILURE;
- var ipAddress = request.GetIPAddress() ?? string.Empty;
- var userInfo = UserController.ValidateUser(
- portalSettings.PortalId,
- loginData.Username, loginData.Password, "DNN", string.Empty, AuthScheme, ipAddress, ref status);
-
- if (userInfo == null)
- {
- Logger.Trace("user = null");
- return EmptyWithError("bad-credentials");
- }
-
- var valid =
- status == UserLoginStatus.LOGIN_SUCCESS ||
- status == UserLoginStatus.LOGIN_SUPERUSER ||
- status == UserLoginStatus.LOGIN_INSECUREADMINPASSWORD ||
- status == UserLoginStatus.LOGIN_INSECUREHOSTPASSWORD;
-
- if (!valid)
- {
- Logger.Trace("login status = " + status);
- return EmptyWithError("bad-credentials");
- }
-
- // save hash values in DB so no one with access can create JWT header from existing data
- var sessionId = NewSessionId;
- var now = DateTime.UtcNow;
- var renewalToken = EncodeBase64(Hasher.ComputeHash(Guid.NewGuid().ToByteArray()));
- var ptoken = new PersistedToken
- {
- TokenId = sessionId,
- UserId = userInfo.UserID,
- TokenExpiry = now.AddMinutes(SessionTokenTtl),
- RenewalExpiry = now.AddDays(RenewalTokenTtl),
- RenewalHash = GetHashedStr(renewalToken),
- };
-
- var secret = ObtainSecret(sessionId, portalSettings.GUID, userInfo.Membership.LastPasswordChangeDate);
- var jwt = CreateJwtToken(secret, portalSettings.PortalAlias.HTTPAlias, ptoken, userInfo.Roles);
- var accessToken = jwt.RawData;
-
- ptoken.TokenHash = GetHashedStr(accessToken);
- this.DataProvider.AddToken(ptoken);
-
- return new LoginResultData
- {
- UserId = userInfo.UserID,
- DisplayName = userInfo.DisplayName,
- AccessToken = accessToken,
- RenewalToken = renewalToken,
- };
- }
-
- public LoginResultData RenewToken(HttpRequestMessage request, string renewalToken)
- {
- if (!JwtAuthMessageHandler.IsEnabled)
- {
- Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
- return EmptyWithError("disabled");
- }
-
- var rawToken = this.ValidateAuthHeader(request?.Headers.Authorization);
- if (string.IsNullOrEmpty(rawToken))
- {
- return EmptyWithError("bad-credentials");
- }
-
- var jwt = GetAndValidateJwt(rawToken, false);
- if (jwt == null)
- {
- return EmptyWithError("bad-jwt");
- }
-
- var sessionId = GetJwtSessionValue(jwt);
- if (string.IsNullOrEmpty(sessionId))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Session ID not found in the claim");
- }
-
- return EmptyWithError("bad-claims");
- }
-
- var ptoken = this.DataProvider.GetTokenById(sessionId);
- if (ptoken == null)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Token not found in DB");
- }
-
- return EmptyWithError("not-found");
- }
-
- if (ptoken.RenewalExpiry <= DateTime.UtcNow)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Token can't bwe renewed anymore");
- }
-
- return EmptyWithError("not-more-renewal");
- }
-
- if (ptoken.RenewalHash != GetHashedStr(renewalToken))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Invalid renewal token");
- }
-
- return EmptyWithError("bad-token");
- }
-
- if (ptoken.TokenHash != GetHashedStr(rawToken))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Invalid access token");
- }
-
- return EmptyWithError("bad-token");
- }
-
- var userInfo = this.TryGetUser(jwt, false);
- if (userInfo == null)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("User not found in DB");
- }
-
- return EmptyWithError("not-found");
- }
-
- if (ptoken.UserId != userInfo.UserID)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Mismatch token and user");
- }
-
- return EmptyWithError("bad-token");
- }
-
- return this.UpdateToken(renewalToken, ptoken, userInfo);
- }
-
- protected override Func GetFactory()
- {
- return () => new JwtController();
- }
-
- private static LoginResultData EmptyWithError(string error)
- {
- return new LoginResultData { Error = error };
- }
-
- private static JwtSecurityToken CreateJwtToken(byte[] symmetricKey, string issuer, PersistedToken ptoken, IEnumerable roles)
- {
- // var key = Convert.FromBase64String(symmetricKey);
- var credentials = new SigningCredentials(
- new InMemorySymmetricSecurityKey(symmetricKey),
- "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
- "http://www.w3.org/2001/04/xmlenc#sha256");
-
- var claimsIdentity = new ClaimsIdentity();
- claimsIdentity.AddClaim(new Claim(SessionClaimType, ptoken.TokenId));
- claimsIdentity.AddClaims(roles.Select(r => new Claim(ClaimTypes.Role, r)));
-
- var notBefore = DateTime.UtcNow.AddMinutes(-ClockSkew);
- var notAfter = ptoken.TokenExpiry;
- var tokenHandler = new JwtSecurityTokenHandler();
- var token = tokenHandler.CreateToken(issuer, null, claimsIdentity, notBefore, notAfter, credentials);
- return token;
- }
-
- private static JwtSecurityToken GetAndValidateJwt(string rawToken, bool checkExpiry)
- {
- JwtSecurityToken jwt;
- try
- {
- jwt = new JwtSecurityToken(rawToken);
- }
- catch (Exception ex)
- {
- Logger.Error("Unable to construct JWT object from authorization value. " + ex.Message);
- return null;
- }
-
- if (checkExpiry)
- {
- var now = DateTime.UtcNow;
- if (now < jwt.ValidFrom || now > jwt.ValidTo)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Token is expired");
- }
-
- return null;
- }
- }
-
- var sessionId = GetJwtSessionValue(jwt);
- if (string.IsNullOrEmpty(sessionId))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Invaid session ID claim");
- }
-
- return null;
- }
-
- return jwt;
- }
-
- private static string GetJwtSessionValue(JwtSecurityToken jwt)
- {
- var sessionClaim = jwt?.Claims?.FirstOrDefault(claim => SessionClaimType.Equals(claim.Type));
- return sessionClaim?.Value;
- }
-
- private static byte[] ObtainSecret(string sessionId, Guid portalGuid, DateTime userCreationDate)
- {
- // The secret should contain unpredictable components that can't be inferred from the JWT string.
- var stext = string.Join(".", sessionId, portalGuid.ToString("N"), userCreationDate.ToUniversalTime().ToString("O"));
- return TextEncoder.GetBytes(stext);
- }
-
- private static string DecodeBase64(string b64Str)
- {
- // fix Base64 string padding
- var mod = b64Str.Length % 4;
- if (mod != 0)
- {
- b64Str += new string('=', 4 - mod);
- }
-
- return TextEncoder.GetString(Convert.FromBase64String(b64Str));
- }
-
- private static string EncodeBase64(byte[] data)
- {
- return Convert.ToBase64String(data).TrimEnd('=');
- }
-
- private static string GetHashedStr(string data)
- {
- return EncodeBase64(Hasher.ComputeHash(TextEncoder.GetBytes(data)));
- }
-
- private LoginResultData UpdateToken(string renewalToken, PersistedToken ptoken, UserInfo userInfo)
- {
- var expiry = DateTime.UtcNow.AddMinutes(SessionTokenTtl);
- if (expiry > ptoken.RenewalExpiry)
- {
- // don't extend beyond renewal expiry and make sure it is marked in UTC
- expiry = new DateTime(ptoken.RenewalExpiry.Ticks, DateTimeKind.Utc);
- }
-
- ptoken.TokenExpiry = expiry;
-
- var portalSettings = PortalController.Instance.GetCurrentPortalSettings();
- var secret = ObtainSecret(ptoken.TokenId, portalSettings.GUID, userInfo.Membership.LastPasswordChangeDate);
- var jwt = CreateJwtToken(secret, portalSettings.PortalAlias.HTTPAlias, ptoken, userInfo.Roles);
- var accessToken = jwt.RawData;
-
- // save hash values in DB so no one with access can create JWT header from existing data
- ptoken.TokenHash = GetHashedStr(accessToken);
- this.DataProvider.UpdateToken(ptoken);
-
- return new LoginResultData
- {
- UserId = userInfo.UserID,
- DisplayName = userInfo.DisplayName,
- AccessToken = accessToken,
- RenewalToken = renewalToken,
- };
- }
-
- ///
- /// Checks for Authorization header and validates it is JWT scheme. If successful, it returns the token string.
- ///
- /// The request auhorization header.
- /// The JWT passed in the request; otherwise, it returns null.
- private string ValidateAuthHeader(AuthenticationHeaderValue authHdr)
- {
- if (authHdr == null)
- {
- // if (Logger.IsTraceEnabled) Logger.Trace("Authorization header not present in the request"); // too verbose; shows in all web requests
- return null;
- }
-
- if (!string.Equals(authHdr.Scheme, AuthScheme, StringComparison.CurrentCultureIgnoreCase))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Authorization header scheme in the request is not equal to " + this.SchemeType);
- }
-
- return null;
- }
-
- var authorization = authHdr.Parameter;
- if (string.IsNullOrEmpty(authorization))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Missing authorization header value in the request");
- }
-
- return null;
- }
-
- return authorization;
- }
-
- private string ValidateAuthorizationValue(string authorization)
- {
- var parts = authorization.Split('.');
- if (parts.Length < 3)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Token must have [header:claims:signature] parts at least");
- }
-
- return null;
- }
-
- var decoded = DecodeBase64(parts[0]);
- if (decoded.IndexOf("\"" + this.SchemeType + "\"", StringComparison.InvariantCultureIgnoreCase) < 0)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace($"This is not a {this.SchemeType} autentication scheme.");
- }
-
- return null;
- }
-
- var header = JsonConvert.DeserializeObject(decoded);
- if (!this.IsValidSchemeType(header))
- {
- return null;
- }
-
- var jwt = GetAndValidateJwt(authorization, true);
- if (jwt == null)
- {
- return null;
- }
-
- var userInfo = this.TryGetUser(jwt, true);
- return userInfo?.Username;
- }
-
- private bool IsValidSchemeType(JwtHeader header)
- {
- if (!this.SchemeType.Equals(header["typ"] as string, StringComparison.OrdinalIgnoreCase))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Unsupported authentication scheme type " + header.Typ);
- }
-
- return false;
- }
-
- return true;
- }
-
- private UserInfo TryGetUser(JwtSecurityToken jwt, bool checkExpiry)
- {
- // validate against DB saved data
- var sessionId = GetJwtSessionValue(jwt);
- var ptoken = this.DataProvider.GetTokenById(sessionId);
- if (ptoken == null)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Token not found in DB");
- }
-
- return null;
- }
-
- if (checkExpiry)
- {
- var now = DateTime.UtcNow;
- if (now > ptoken.TokenExpiry || now > ptoken.RenewalExpiry)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("DB Token is expired");
- }
-
- return null;
- }
- }
-
- if (ptoken.TokenHash != GetHashedStr(jwt.RawData))
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Mismatch data in received token");
- }
-
- return null;
- }
-
- var portalSettings = PortalController.Instance.GetCurrentPortalSettings();
- if (portalSettings == null)
- {
- Logger.Trace("Unable to retrieve portal settings");
- return null;
- }
-
- var userInfo = UserController.GetUserById(portalSettings.PortalId, ptoken.UserId);
- if (userInfo == null)
- {
- if (Logger.IsTraceEnabled)
- {
- Logger.Trace("Invalid user");
- }
-
- return null;
- }
-
- var status = UserController.ValidateUser(userInfo, portalSettings.PortalId, false);
- var valid =
- status == UserValidStatus.VALID ||
- status == UserValidStatus.UPDATEPROFILE ||
- status == UserValidStatus.UPDATEPASSWORD;
-
- if (!valid && Logger.IsTraceEnabled)
- {
- Logger.Trace("Inactive user status: " + status);
- return null;
- }
-
- return userInfo;
- }
- }
-}
+namespace Dnn.AuthServices.Jwt.Components.Common.Controllers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IdentityModel.Tokens;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Security.Claims;
+ using System.Security.Cryptography;
+ using System.Text;
+
+ using Dnn.AuthServices.Jwt.Auth;
+ using Dnn.AuthServices.Jwt.Components.Entity;
+ using Dnn.AuthServices.Jwt.Data;
+ using DotNetNuke.Abstractions.Portals;
+ using DotNetNuke.Common;
+ using DotNetNuke.Entities.Portals;
+ using DotNetNuke.Entities.Users;
+ using DotNetNuke.Framework;
+ using DotNetNuke.Instrumentation;
+ using DotNetNuke.Security.Membership;
+ using DotNetNuke.Web.Api;
+ using Newtonsoft.Json;
+
+ ///
+ /// Controls JWT features.
+ ///
+ internal class JwtController : ServiceLocator, IJwtController
+ {
+ ///
+ /// The name of the authentication scheme header.
+ ///
+ public const string AuthScheme = "Bearer";
+
+ ///
+ /// A reference to the Dnn data provider.
+ ///
+ public readonly IDataService DataProvider = DataService.Instance;
+
+ private const int ClockSkew = 5; // in minutes; default for clock skew
+ private const int SessionTokenTtl = 60; // in minutes = 1 hour
+
+ private const int RenewalTokenTtl = 14; // in days = 2 weeks
+ private const string SessionClaimType = "sid";
+
+ private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(JwtController));
+ private static readonly HashAlgorithm Hasher = SHA384.Create();
+ private static readonly Encoding TextEncoder = Encoding.UTF8;
+
+ ///
+ public string SchemeType => "JWT";
+
+ private static string NewSessionId => DateTime.UtcNow.Ticks.ToString("x16") + Guid.NewGuid().ToString("N").Substring(16);
+
+ ///
+ public string ValidateToken(HttpRequestMessage request)
+ {
+ if (!JwtAuthMessageHandler.IsEnabled)
+ {
+ Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
+ return null;
+ }
+
+ var authorization = this.ValidateAuthHeader(request?.Headers.Authorization);
+ return string.IsNullOrEmpty(authorization) ? null : this.ValidateAuthorizationValue(authorization);
+ }
+
+ ///
+ public bool LogoutUser(HttpRequestMessage request)
+ {
+ if (!JwtAuthMessageHandler.IsEnabled)
+ {
+ Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
+ return false;
+ }
+
+ var rawToken = this.ValidateAuthHeader(request?.Headers.Authorization);
+ if (string.IsNullOrEmpty(rawToken))
+ {
+ return false;
+ }
+
+ var jwt = new JwtSecurityToken(rawToken);
+ var sessionId = GetJwtSessionValue(jwt);
+ if (string.IsNullOrEmpty(sessionId))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Session ID not found in the claim");
+ }
+
+ return false;
+ }
+
+ this.DataProvider.DeleteToken(sessionId);
+ return true;
+ }
+
+ ///
+ public LoginResultData LoginUser(HttpRequestMessage request, LoginData loginData)
+ {
+ if (!JwtAuthMessageHandler.IsEnabled)
+ {
+ Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
+ return EmptyWithError("disabled");
+ }
+
+ var portalSettings = PortalController.Instance.GetCurrentPortalSettings();
+ if (portalSettings == null)
+ {
+ Logger.Trace("portalSettings = null");
+ return EmptyWithError("no-portal");
+ }
+
+ var status = UserLoginStatus.LOGIN_FAILURE;
+ var ipAddress = request.GetIPAddress() ?? string.Empty;
+ var userInfo = UserController.ValidateUser(
+ portalSettings.PortalId,
+ loginData.Username,
+ loginData.Password,
+ "DNN",
+ string.Empty,
+ AuthScheme,
+ ipAddress,
+ ref status);
+
+ if (userInfo == null)
+ {
+ Logger.Trace("user = null");
+ return EmptyWithError("bad-credentials");
+ }
+
+ var valid =
+ status == UserLoginStatus.LOGIN_SUCCESS ||
+ status == UserLoginStatus.LOGIN_SUPERUSER ||
+ status == UserLoginStatus.LOGIN_INSECUREADMINPASSWORD ||
+ status == UserLoginStatus.LOGIN_INSECUREHOSTPASSWORD;
+
+ if (!valid)
+ {
+ Logger.Trace("login status = " + status);
+ return EmptyWithError("bad-credentials");
+ }
+
+ // save hash values in DB so no one with access can create JWT header from existing data
+ var sessionId = NewSessionId;
+ var now = DateTime.UtcNow;
+ var renewalToken = EncodeBase64(Hasher.ComputeHash(Guid.NewGuid().ToByteArray()));
+ var ptoken = new PersistedToken
+ {
+ TokenId = sessionId,
+ UserId = userInfo.UserID,
+ TokenExpiry = now.AddMinutes(SessionTokenTtl),
+ RenewalExpiry = now.AddDays(RenewalTokenTtl),
+ RenewalHash = GetHashedStr(renewalToken),
+ };
+
+ var secret = ObtainSecret(sessionId, portalSettings.GUID, userInfo.Membership.LastPasswordChangeDate);
+ var jwt = CreateJwtToken(
+ secret,
+ portalSettings.PortalAlias.HTTPAlias,
+ ptoken,
+ userInfo.Roles);
+ var accessToken = jwt.RawData;
+
+ ptoken.TokenHash = GetHashedStr(accessToken);
+ this.DataProvider.AddToken(ptoken);
+
+ return new LoginResultData
+ {
+ UserId = userInfo.UserID,
+ DisplayName = userInfo.DisplayName,
+ AccessToken = accessToken,
+ RenewalToken = renewalToken,
+ };
+ }
+
+ ///
+ public LoginResultData RenewToken(HttpRequestMessage request, string renewalToken)
+ {
+ if (!JwtAuthMessageHandler.IsEnabled)
+ {
+ Logger.Trace(this.SchemeType + " is not registered/enabled in web.config file");
+ return EmptyWithError("disabled");
+ }
+
+ var rawToken = this.ValidateAuthHeader(request?.Headers.Authorization);
+ if (string.IsNullOrEmpty(rawToken))
+ {
+ return EmptyWithError("bad-credentials");
+ }
+
+ var jwt = GetAndValidateJwt(rawToken, false);
+ if (jwt == null)
+ {
+ return EmptyWithError("bad-jwt");
+ }
+
+ var sessionId = GetJwtSessionValue(jwt);
+ if (string.IsNullOrEmpty(sessionId))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Session ID not found in the claim");
+ }
+
+ return EmptyWithError("bad-claims");
+ }
+
+ var ptoken = this.DataProvider.GetTokenById(sessionId);
+ if (ptoken == null)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Token not found in DB");
+ }
+
+ return EmptyWithError("not-found");
+ }
+
+ if (ptoken.RenewalExpiry <= DateTime.UtcNow)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Token can't bwe renewed anymore");
+ }
+
+ return EmptyWithError("not-more-renewal");
+ }
+
+ if (ptoken.RenewalHash != GetHashedStr(renewalToken))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Invalid renewal token");
+ }
+
+ return EmptyWithError("bad-token");
+ }
+
+ if (ptoken.TokenHash != GetHashedStr(rawToken))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Invalid access token");
+ }
+
+ return EmptyWithError("bad-token");
+ }
+
+ var userInfo = this.TryGetUser(jwt, false);
+ if (userInfo == null)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("User not found in DB");
+ }
+
+ return EmptyWithError("not-found");
+ }
+
+ if (ptoken.UserId != userInfo.UserID)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Mismatch token and user");
+ }
+
+ return EmptyWithError("bad-token");
+ }
+
+ return this.UpdateToken(renewalToken, ptoken, userInfo);
+ }
+
+ ///
+ protected override Func GetFactory()
+ {
+ return () => new JwtController();
+ }
+
+ private static LoginResultData EmptyWithError(string error)
+ {
+ return new LoginResultData { Error = error };
+ }
+
+ private static JwtSecurityToken CreateJwtToken(byte[] symmetricKey, string issuer, PersistedToken ptoken, IEnumerable roles)
+ {
+ // var key = Convert.FromBase64String(symmetricKey);
+ var credentials = new SigningCredentials(
+ new InMemorySymmetricSecurityKey(symmetricKey),
+ "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
+ "http://www.w3.org/2001/04/xmlenc#sha256");
+
+ var claimsIdentity = new ClaimsIdentity();
+ claimsIdentity.AddClaim(new Claim(SessionClaimType, ptoken.TokenId));
+ claimsIdentity.AddClaims(roles.Select(r => new Claim(ClaimTypes.Role, r)));
+
+ var notBefore = DateTime.UtcNow.AddMinutes(-ClockSkew);
+ var notAfter = ptoken.TokenExpiry;
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var token = tokenHandler.CreateToken(issuer, null, claimsIdentity, notBefore, notAfter, credentials);
+ return token;
+ }
+
+ private static JwtSecurityToken GetAndValidateJwt(string rawToken, bool checkExpiry)
+ {
+ JwtSecurityToken jwt;
+ try
+ {
+ jwt = new JwtSecurityToken(rawToken);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error("Unable to construct JWT object from authorization value. " + ex.Message);
+ return null;
+ }
+
+ if (checkExpiry)
+ {
+ var now = DateTime.UtcNow;
+ if (now < jwt.ValidFrom || now > jwt.ValidTo)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Token is expired");
+ }
+
+ return null;
+ }
+ }
+
+ var sessionId = GetJwtSessionValue(jwt);
+ if (string.IsNullOrEmpty(sessionId))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Invaid session ID claim");
+ }
+
+ return null;
+ }
+
+ return jwt;
+ }
+
+ private static string GetJwtSessionValue(JwtSecurityToken jwt)
+ {
+ var sessionClaim = jwt?.Claims?.FirstOrDefault(claim => SessionClaimType.Equals(claim.Type));
+ return sessionClaim?.Value;
+ }
+
+ private static byte[] ObtainSecret(string sessionId, Guid portalGuid, DateTime userCreationDate)
+ {
+ // The secret should contain unpredictable components that can't be inferred from the JWT string.
+ var stext = string.Join(".", sessionId, portalGuid.ToString("N"), userCreationDate.ToUniversalTime().ToString("O"));
+ return TextEncoder.GetBytes(stext);
+ }
+
+ private static string DecodeBase64(string b64Str)
+ {
+ // fix Base64 string padding
+ var mod = b64Str.Length % 4;
+ if (mod != 0)
+ {
+ b64Str += new string('=', 4 - mod);
+ }
+
+ return TextEncoder.GetString(Convert.FromBase64String(b64Str));
+ }
+
+ private static string EncodeBase64(byte[] data)
+ {
+ return Convert.ToBase64String(data).TrimEnd('=');
+ }
+
+ private static string GetHashedStr(string data)
+ {
+ return EncodeBase64(Hasher.ComputeHash(TextEncoder.GetBytes(data)));
+ }
+
+ private LoginResultData UpdateToken(string renewalToken, PersistedToken ptoken, UserInfo userInfo)
+ {
+ var expiry = DateTime.UtcNow.AddMinutes(SessionTokenTtl);
+ if (expiry > ptoken.RenewalExpiry)
+ {
+ // don't extend beyond renewal expiry and make sure it is marked in UTC
+ expiry = new DateTime(ptoken.RenewalExpiry.Ticks, DateTimeKind.Utc);
+ }
+
+ ptoken.TokenExpiry = expiry;
+
+ var portalSettings = PortalController.Instance.GetCurrentPortalSettings();
+ var secret = ObtainSecret(ptoken.TokenId, portalSettings.GUID, userInfo.Membership.LastPasswordChangeDate);
+ var jwt = CreateJwtToken(secret, portalSettings.PortalAlias.HTTPAlias, ptoken, userInfo.Roles);
+ var accessToken = jwt.RawData;
+
+ // save hash values in DB so no one with access can create JWT header from existing data
+ ptoken.TokenHash = GetHashedStr(accessToken);
+ this.DataProvider.UpdateToken(ptoken);
+
+ return new LoginResultData
+ {
+ UserId = userInfo.UserID,
+ DisplayName = userInfo.DisplayName,
+ AccessToken = accessToken,
+ RenewalToken = renewalToken,
+ };
+ }
+
+ ///
+ /// Checks for Authorization header and validates it is JWT scheme. If successful, it returns the token string.
+ ///
+ /// The request auhorization header.
+ /// The JWT passed in the request; otherwise, it returns null.
+ private string ValidateAuthHeader(AuthenticationHeaderValue authHdr)
+ {
+ if (authHdr == null)
+ {
+ // if (Logger.IsTraceEnabled) Logger.Trace("Authorization header not present in the request"); // too verbose; shows in all web requests
+ return null;
+ }
+
+ if (!string.Equals(authHdr.Scheme, AuthScheme, StringComparison.CurrentCultureIgnoreCase))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Authorization header scheme in the request is not equal to " + this.SchemeType);
+ }
+
+ return null;
+ }
+
+ var authorization = authHdr.Parameter;
+ if (string.IsNullOrEmpty(authorization))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Missing authorization header value in the request");
+ }
+
+ return null;
+ }
+
+ return authorization;
+ }
+
+ private string ValidateAuthorizationValue(string authorization)
+ {
+ var parts = authorization.Split('.');
+ if (parts.Length < 3)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Token must have [header:claims:signature] parts at least");
+ }
+
+ return null;
+ }
+
+ var decoded = DecodeBase64(parts[0]);
+ if (decoded.IndexOf("\"" + this.SchemeType + "\"", StringComparison.InvariantCultureIgnoreCase) < 0)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace($"This is not a {this.SchemeType} autentication scheme.");
+ }
+
+ return null;
+ }
+
+ var header = JsonConvert.DeserializeObject(decoded);
+ if (!this.IsValidSchemeType(header))
+ {
+ return null;
+ }
+
+ var jwt = GetAndValidateJwt(authorization, true);
+ if (jwt == null)
+ {
+ return null;
+ }
+
+ var userInfo = this.TryGetUser(jwt, true);
+ return userInfo?.Username;
+ }
+
+ private bool IsValidSchemeType(JwtHeader header)
+ {
+ if (!this.SchemeType.Equals(header["typ"] as string, StringComparison.OrdinalIgnoreCase))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Unsupported authentication scheme type " + header.Typ);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private UserInfo TryGetUser(JwtSecurityToken jwt, bool checkExpiry)
+ {
+ // validate against DB saved data
+ var sessionId = GetJwtSessionValue(jwt);
+ var ptoken = this.DataProvider.GetTokenById(sessionId);
+ if (ptoken == null)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Token not found in DB");
+ }
+
+ return null;
+ }
+
+ if (checkExpiry)
+ {
+ var now = DateTime.UtcNow;
+ if (now > ptoken.TokenExpiry || now > ptoken.RenewalExpiry)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("DB Token is expired");
+ }
+
+ return null;
+ }
+ }
+
+ if (ptoken.TokenHash != GetHashedStr(jwt.RawData))
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Mismatch data in received token");
+ }
+
+ return null;
+ }
+
+ var portalSettings = PortalController.Instance.GetCurrentSettings();
+ if (portalSettings == null)
+ {
+ Logger.Trace("Unable to retrieve portal settings");
+ return null;
+ }
+
+ var userInfo = UserController.GetUserById(portalSettings.PortalId, ptoken.UserId);
+ if (userInfo == null)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Invalid user");
+ }
+
+ return null;
+ }
+
+ var status = UserController.ValidateUser(userInfo, portalSettings.PortalId, false);
+ var valid =
+ status == UserValidStatus.VALID ||
+ status == UserValidStatus.UPDATEPROFILE ||
+ status == UserValidStatus.UPDATEPASSWORD;
+
+ if (!valid)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Inactive user status: " + status);
+ }
+
+ return null;
+ }
+
+ if (!userInfo.Membership.Approved)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Non Approved user id: " + userInfo.UserID + " UserName: " + userInfo.Username);
+ }
+
+ return null;
+ }
+
+ if (userInfo.IsDeleted)
+ {
+ if (Logger.IsTraceEnabled)
+ {
+ Logger.Trace("Deleted user id: " + userInfo.UserID + " UserName: " + userInfo.Username);
+ }
+
+ return null;
+ }
+
+ return userInfo;
+ }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginData.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginData.cs
index 61bb48f3d24..a40bb397b38 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginData.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginData.cs
@@ -2,20 +2,26 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Entity
-{
- using Newtonsoft.Json;
+namespace Dnn.AuthServices.Jwt.Components.Entity
+{
+ using Newtonsoft.Json;
- ///
- /// Structure used for the Login to obtain a Json Web Token (JWT).
- ///
- [JsonObject]
- public struct LoginData
- {
- [JsonProperty("u")]
- public string Username;
-
- [JsonProperty("p")]
- public string Password;
- }
-}
+ ///
+ /// Structure used for the Login to obtain a Json Web Token (JWT).
+ ///
+ [JsonObject]
+ public struct LoginData
+ {
+ ///
+ /// The authentication username.
+ ///
+ [JsonProperty("u")]
+ public string Username;
+
+ ///
+ /// The authentication password.
+ ///
+ [JsonProperty("p")]
+ public string Password;
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginResultData.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginResultData.cs
index 4a5a8353431..6d097e95dd7 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginResultData.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/LoginResultData.cs
@@ -2,26 +2,44 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Entity
-{
- using Newtonsoft.Json;
+namespace Dnn.AuthServices.Jwt.Components.Entity
+{
+ using Newtonsoft.Json;
- [JsonObject]
- public class LoginResultData
- {
- [JsonProperty("userId")]
- public int UserId { get; set; }
-
- [JsonProperty("displayName")]
- public string DisplayName { get; set; }
-
- [JsonProperty("accessToken")]
- public string AccessToken { get; set; }
-
- [JsonProperty("renewalToken")]
- public string RenewalToken { get; set; }
-
- [JsonIgnore]
- public string Error { get; set; }
- }
-}
+ ///
+ /// Represents information about a login result.
+ ///
+ [JsonObject]
+ public class LoginResultData
+ {
+ ///
+ /// Gets or sets the id of the user.
+ ///
+ [JsonProperty("userId")]
+ public int UserId { get; set; }
+
+ ///
+ /// Gets or sets the user display name.
+ ///
+ [JsonProperty("displayName")]
+ public string DisplayName { get; set; }
+
+ ///
+ /// Gets or sets the access token.
+ ///
+ [JsonProperty("accessToken")]
+ public string AccessToken { get; set; }
+
+ ///
+ /// Gets or sets the renewal token.
+ ///
+ [JsonProperty("renewalToken")]
+ public string RenewalToken { get; set; }
+
+ ///
+ /// Gets or sets any error message.
+ ///
+ [JsonIgnore]
+ public string Error { get; set; }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/PersistedToken.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/PersistedToken.cs
index a2f14d5eec7..73f025904eb 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/PersistedToken.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/PersistedToken.cs
@@ -2,25 +2,49 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Entity
-{
- using System;
-
- [Serializable]
- public class PersistedToken
- {
- public string TokenId { get; set; }
-
- public int UserId { get; set; }
-
- public int RenewCount { get; set; }
-
- public DateTime TokenExpiry { get; set; }
-
- public DateTime RenewalExpiry { get; set; }
-
- public string TokenHash { get; set; }
-
- public string RenewalHash { get; set; }
- }
-}
+namespace Dnn.AuthServices.Jwt.Components.Entity
+{
+ using System;
+
+ ///
+ /// Represents a persisted token.
+ ///
+ [Serializable]
+ public class PersistedToken
+ {
+ ///
+ /// Gets or sets the ID for the token.
+ ///
+ public string TokenId { get; set; }
+
+ ///
+ /// Gets or sets the id of the user.
+ ///
+ public int UserId { get; set; }
+
+ ///
+ /// Gets or sets the renewal count.
+ ///
+ public int RenewCount { get; set; }
+
+ ///
+ /// Gets or sets a value indicating when the token expires.
+ ///
+ public DateTime TokenExpiry { get; set; }
+
+ ///
+ /// Gets or sets when the renewal token expires.
+ ///
+ public DateTime RenewalExpiry { get; set; }
+
+ ///
+ /// Gets or sets the token hash value.
+ ///
+ public string TokenHash { get; set; }
+
+ ///
+ /// Gets or sets the renewal token hash value.
+ ///
+ public string RenewalHash { get; set; }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/RenewalDto.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/RenewalDto.cs
index 5c64b7a6a57..cecd322ccd5 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/RenewalDto.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Entity/RenewalDto.cs
@@ -2,14 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Components.Entity
-{
- using Newtonsoft.Json;
+namespace Dnn.AuthServices.Jwt.Components.Entity
+{
+ using Newtonsoft.Json;
- [JsonObject]
- public class RenewalDto
- {
- [JsonProperty("rtoken")]
- public string RenewalToken;
- }
-}
+ ///
+ /// Renewal token data transfer object.
+ ///
+ [JsonObject]
+ public class RenewalDto
+ {
+ ///
+ /// A string representing the renewal token.
+ ///
+ [JsonProperty("rtoken")]
+ public string RenewalToken;
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Components/Schedule/PurgeExpiredTokensTask.cs b/DNN Platform/Dnn.AuthServices.Jwt/Components/Schedule/PurgeExpiredTokensTask.cs
index 2c329d7a3e7..6640ec9c073 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Components/Schedule/PurgeExpiredTokensTask.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Components/Schedule/PurgeExpiredTokensTask.cs
@@ -12,7 +12,7 @@ namespace Dnn.AuthServices.Jwt.Components.Schedule
using DotNetNuke.Services.Scheduling;
///
- /// Scheduled task to delete tokens that linger in the database after having expired
+ /// Scheduled task to delete tokens that linger in the database after having expired.
///
public class PurgeExpiredTokensTask : SchedulerClient
{
@@ -21,7 +21,7 @@ public class PurgeExpiredTokensTask : SchedulerClient
///
/// Initializes a new instance of the class.
///
- /// The object used to record the results from this task
+ /// The object used to record the results from this task.
public PurgeExpiredTokensTask(ScheduleHistoryItem objScheduleHistoryItem)
{
this.ScheduleHistoryItem = objScheduleHistoryItem;
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Data/DataService.cs b/DNN Platform/Dnn.AuthServices.Jwt/Data/DataService.cs
index f42faaecd62..27ea2c4a515 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Data/DataService.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Data/DataService.cs
@@ -2,83 +2,95 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Data
-{
- using System;
- using System.Collections.Generic;
- using System.Web.Caching;
+namespace Dnn.AuthServices.Jwt.Data
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Web.Caching;
- using Dnn.AuthServices.Jwt.Components.Entity;
- using DotNetNuke.Common.Utilities;
- using DotNetNuke.ComponentModel;
- using DotNetNuke.Data;
+ using Dnn.AuthServices.Jwt.Components.Entity;
+ using DotNetNuke.Common.Utilities;
+ using DotNetNuke.ComponentModel;
+ using DotNetNuke.Data;
- /// -----------------------------------------------------------------------------
- ///
- /// This class provides the Data Access Layer for the JWT Authentication library.
- ///
- public class DataService : ComponentBase, IDataService
- {
- private readonly DataProvider _dataProvider = DataProvider.Instance();
-
- public virtual PersistedToken GetTokenById(string tokenId)
- {
- try
- {
- return CBO.GetCachedObject(
- new CacheItemArgs(GetCacheKey(tokenId), 60, CacheItemPriority.Default),
- _ => CBO.FillObject(this._dataProvider.ExecuteReader("JsonWebTokens_GetById", tokenId)));
- }
- catch (InvalidCastException)
- {
- // occurs when no record found in th DB
- return null;
- }
- }
-
- public virtual IList GetUserTokens(int userId)
- {
- return CBO.FillCollection(this._dataProvider.ExecuteReader("JsonWebTokens_GetByUserId", userId));
- }
-
- public virtual void AddToken(PersistedToken token)
- {
- this._dataProvider.ExecuteNonQuery("JsonWebTokens_Add", token.TokenId, token.UserId,
- token.TokenExpiry, token.RenewalExpiry, token.TokenHash, token.RenewalHash);
- DataCache.SetCache(GetCacheKey(token.TokenId), token, token.TokenExpiry.ToLocalTime());
- }
-
- public virtual void UpdateToken(PersistedToken token)
- {
- this._dataProvider.ExecuteNonQuery("JsonWebTokens_Update", token.TokenId, token.TokenExpiry, token.TokenHash);
- token.RenewCount += 1;
- DataCache.SetCache(GetCacheKey(token.TokenId), token, token.TokenExpiry.ToLocalTime());
- }
-
- public virtual void DeleteToken(string tokenId)
- {
- this._dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteById", tokenId);
- DataCache.RemoveCache(GetCacheKey(tokenId));
- }
-
- public virtual void DeleteUserTokens(int userId)
- {
- this._dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteByUser", userId);
- foreach (var token in this.GetUserTokens(userId))
- {
- DataCache.RemoveCache(GetCacheKey(token.TokenId));
- }
- }
-
- public virtual void DeleteExpiredTokens()
- {
- // don't worry about caching; these will already be invalidated by cache manager
- this._dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteExpired");
- }
-
- private static string GetCacheKey(string tokenId)
- {
- return string.Join(":", "JsonWebTokens", tokenId);
- }
- }
-}
+ ///
+ /// This class provides the Data Access Layer for the JWT Authentication library.
+ ///
+ public class DataService : ComponentBase, IDataService
+ {
+ private readonly DataProvider dataProvider = DataProvider.Instance();
+
+ ///
+ public virtual PersistedToken GetTokenById(string tokenId)
+ {
+ try
+ {
+ return CBO.GetCachedObject(
+ new CacheItemArgs(GetCacheKey(tokenId), 60, CacheItemPriority.Default),
+ _ => CBO.FillObject(this.dataProvider.ExecuteReader("JsonWebTokens_GetById", tokenId)));
+ }
+ catch (InvalidCastException)
+ {
+ // occurs when no record found in th DB
+ return null;
+ }
+ }
+
+ ///
+ public virtual IList GetUserTokens(int userId)
+ {
+ return CBO.FillCollection(this.dataProvider.ExecuteReader("JsonWebTokens_GetByUserId", userId));
+ }
+
+ ///
+ public virtual void AddToken(PersistedToken token)
+ {
+ this.dataProvider.ExecuteNonQuery(
+ "JsonWebTokens_Add",
+ token.TokenId,
+ token.UserId,
+ token.TokenExpiry,
+ token.RenewalExpiry,
+ token.TokenHash,
+ token.RenewalHash);
+ DataCache.SetCache(GetCacheKey(token.TokenId), token, token.TokenExpiry.ToLocalTime());
+ }
+
+ ///
+ public virtual void UpdateToken(PersistedToken token)
+ {
+ this.dataProvider.ExecuteNonQuery("JsonWebTokens_Update", token.TokenId, token.TokenExpiry, token.TokenHash);
+ token.RenewCount += 1;
+ DataCache.SetCache(GetCacheKey(token.TokenId), token, token.TokenExpiry.ToLocalTime());
+ }
+
+ ///
+ public virtual void DeleteToken(string tokenId)
+ {
+ this.dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteById", tokenId);
+ DataCache.RemoveCache(GetCacheKey(tokenId));
+ }
+
+ ///
+ public virtual void DeleteUserTokens(int userId)
+ {
+ this.dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteByUser", userId);
+ foreach (var token in this.GetUserTokens(userId))
+ {
+ DataCache.RemoveCache(GetCacheKey(token.TokenId));
+ }
+ }
+
+ ///
+ public virtual void DeleteExpiredTokens()
+ {
+ // don't worry about caching; these will already be invalidated by cache manager
+ this.dataProvider.ExecuteNonQuery("JsonWebTokens_DeleteExpired");
+ }
+
+ private static string GetCacheKey(string tokenId)
+ {
+ return string.Join(":", "JsonWebTokens", tokenId);
+ }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Data/IDataService.cs b/DNN Platform/Dnn.AuthServices.Jwt/Data/IDataService.cs
index 440ea428650..8bdd7b7e109 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Data/IDataService.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Data/IDataService.cs
@@ -8,20 +8,52 @@ namespace Dnn.AuthServices.Jwt.Data
using Dnn.AuthServices.Jwt.Components.Entity;
+ ///
+ /// Provides data access services.
+ ///
public interface IDataService
- {
+ {
+ ///
+ /// Gets a token by a given token id.
+ ///
+ /// The token id.
+ /// .
PersistedToken GetTokenById(string tokenId);
-
+
+ ///
+ /// Gets a user token by the user id.
+ ///
+ /// The id of the user.
+ /// A list of tokens.
IList GetUserTokens(int userId);
-
+
+ ///
+ /// Adds (persists) a token.
+ ///
+ /// The token to persist.
void AddToken(PersistedToken token);
-
+
+ ///
+ /// Updates an existing token.
+ ///
+ /// The token to persist.
void UpdateToken(PersistedToken token);
-
+
+ ///
+ /// Deletes an existing token.
+ ///
+ /// The id of the token to delete.
void DeleteToken(string tokenId);
-
+
+ ///
+ /// Deletes all tokens for a user.
+ ///
+ /// The id of user for which to delete the tokens.
void DeleteUserTokens(int userId);
-
+
+ ///
+ /// Deletes the expired tokens.
+ ///
void DeleteExpiredTokens();
}
}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Dnn.AuthServices.Jwt.csproj b/DNN Platform/Dnn.AuthServices.Jwt/Dnn.AuthServices.Jwt.csproj
index b8b348b859c..c67c94d060b 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Dnn.AuthServices.Jwt.csproj
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Dnn.AuthServices.Jwt.csproj
@@ -99,6 +99,10 @@
+
+ {6928A9B1-F88A-4581-A132-D3EB38669BB0}
+ DotNetNuke.Abstractions
+
{3cd5f6b8-8360-4862-80b6-f402892db7dd}
DotNetNuke.Instrumentation
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Library.build b/DNN Platform/Dnn.AuthServices.Jwt/Library.build
index e6bc4c9dcb0..e6450070b98 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Library.build
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Library.build
@@ -4,7 +4,7 @@
- resources
+ zip
Dnn.Jwt
DnnJwtAuth
$(WebsitePath)\DesktopModules\AuthenticationServices\JWTAuth
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Services/MobileController.cs b/DNN Platform/Dnn.AuthServices.Jwt/Services/MobileController.cs
index 89e508a9958..f62c5800a46 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Services/MobileController.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Services/MobileController.cs
@@ -2,104 +2,122 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Services
-{
- using System.Net.Http.Headers;
- using System.Web.Http;
+namespace Dnn.AuthServices.Jwt.Services
+{
+ using System.Net.Http.Headers;
+ using System.Web.Http;
- using Dnn.AuthServices.Jwt.Components.Common.Controllers;
- using Dnn.AuthServices.Jwt.Components.Entity;
- using DotNetNuke.Web.Api;
- using Newtonsoft.Json;
+ using Dnn.AuthServices.Jwt.Components.Common.Controllers;
+ using Dnn.AuthServices.Jwt.Components.Entity;
+ using DotNetNuke.Web.Api;
+ using Newtonsoft.Json;
- [DnnAuthorize(AuthTypes = "JWT")]
- public class MobileController : DnnApiController
- {
- ///
- /// Clients that used JWT login should use this API call to logout and invalidate the tokens.
- ///
- ///
- [HttpGet]
- public IHttpActionResult Logout()
- {
- return JwtController.Instance.LogoutUser(this.Request) ? (IHttpActionResult)this.Ok(new { success = true }) : this.Unauthorized();
- }
-
- ///
- /// Clients that want to go cookie-less should call this API to login and receive
- /// a Json Web Token (JWT) that allows them to authenticate the users to other
- /// secure API endpoints afterwards.
- ///
- /// AllowAnonymous attribute must stay in this call even though the
- /// DnnAuthorize attribute is present at a class level.
- ///
- [HttpPost]
- [AllowAnonymous]
- public IHttpActionResult Login(LoginData loginData)
- {
- var result = JwtController.Instance.LoginUser(this.Request, loginData);
- return this.ReplyWith(result);
- }
-
- ///
- /// Extends the token expiry. A new JWT is returned to the caller which must be used in
- /// new API requests. The caller must pass the renewal token received at the login time.
- /// The header still needs to pass the current token for validation even when it is expired.
- ///
- /// The access token is allowed to get renewed one time only.
- /// AllowAnonymous attribute must stay in this call even though the
- /// DnnAuthorize attribute is present at a class level.
- ///
- ///
- [HttpPost]
- [AllowAnonymous]
- public IHttpActionResult ExtendToken(RenewalDto rtoken)
- {
- var result = JwtController.Instance.RenewToken(this.Request, rtoken.RenewalToken);
- return this.ReplyWith(result);
- }
-
- // Test API Method 1
- [HttpGet]
- public IHttpActionResult TestGet()
- {
- var identity = System.Threading.Thread.CurrentPrincipal.Identity;
- var reply = $"Hello {identity.Name}! You are authenticated through {identity.AuthenticationType}.";
- return this.Ok(new { reply });
- }
-
- // Test API Method 2
- [HttpPost]
- [ValidateAntiForgeryToken]
- public IHttpActionResult TestPost(TestPostData something)
- {
- var identity = System.Threading.Thread.CurrentPrincipal.Identity;
- var reply = $"Hello {identity.Name}! You are authenticated through {identity.AuthenticationType}." +
- $" You said: ({something.Text})";
- return this.Ok(new { reply });
- }
-
- private IHttpActionResult ReplyWith(LoginResultData result)
- {
- if (result == null)
- {
- return this.Unauthorized();
- }
-
- if (!string.IsNullOrEmpty(result.Error))
- {
- // HACK: this will return the scheme with the error message as a challenge; non-standard method
- return this.Unauthorized(new AuthenticationHeaderValue(JwtController.AuthScheme, result.Error));
- }
-
- return this.Ok(result);
- }
-
- [JsonObject]
- public class TestPostData
- {
- [JsonProperty("text")]
- public string Text;
- }
- }
-}
+ ///
+ /// API controller for JWT services (usually mobile).
+ ///
+ [DnnAuthorize(AuthTypes = "JWT")]
+ public class MobileController : DnnApiController
+ {
+ ///
+ /// Clients that used JWT login should use this API call to logout and invalidate the tokens.
+ ///
+ /// An asynchronous HTTP response.
+ [HttpGet]
+ public IHttpActionResult Logout()
+ {
+ return JwtController.Instance.LogoutUser(this.Request) ? (IHttpActionResult)this.Ok(new { success = true }) : this.Unauthorized();
+ }
+
+ ///
+ /// Clients that want to go cookie-less should call this API to login and receive
+ /// a Json Web Token (JWT) that allows them to authenticate the users to other
+ /// secure API endpoints afterwards.
+ ///
+ /// AllowAnonymous attribute must stay in this call even though the
+ /// DnnAuthorize attribute is present at a class level.
+ /// The information usd for login, .
+ /// An asynchronous HTTP response.
+ [HttpPost]
+ [AllowAnonymous]
+ public IHttpActionResult Login(LoginData loginData)
+ {
+ var result = JwtController.Instance.LoginUser(this.Request, loginData);
+ return this.ReplyWith(result);
+ }
+
+ ///
+ /// Extends the token expiry. A new JWT is returned to the caller which must be used in
+ /// new API requests. The caller must pass the renewal token received at the login time.
+ /// The header still needs to pass the current token for validation even when it is expired.
+ ///
+ /// The access token is allowed to get renewed one time only.
+ /// AllowAnonymous attribute must stay in this call even though the
+ /// DnnAuthorize attribute is present at a class level.
+ ///
+ /// The renewal token information, .
+ /// An asynchronous HTTP response.
+ [HttpPost]
+ [AllowAnonymous]
+ public IHttpActionResult ExtendToken(RenewalDto rtoken)
+ {
+ var result = JwtController.Instance.RenewToken(this.Request, rtoken.RenewalToken);
+ return this.ReplyWith(result);
+ }
+
+ ///
+ /// Tests a get HTTP request.
+ ///
+ /// Basic information about the identity.
+ [HttpGet]
+ public IHttpActionResult TestGet()
+ {
+ var identity = System.Threading.Thread.CurrentPrincipal.Identity;
+ var reply = $"Hello {identity.Name}! You are authenticated through {identity.AuthenticationType}.";
+ return this.Ok(new { reply });
+ }
+
+ ///
+ /// Tests a POST api method.
+ ///
+ /// .
+ /// Basic information about the identity and the text provided in the POST.
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public IHttpActionResult TestPost(TestPostData something)
+ {
+ var identity = System.Threading.Thread.CurrentPrincipal.Identity;
+ var reply = $"Hello {identity.Name}! You are authenticated through {identity.AuthenticationType}." +
+ $" You said: ({something.Text})";
+ return this.Ok(new { reply });
+ }
+
+ private IHttpActionResult ReplyWith(LoginResultData result)
+ {
+ if (result == null)
+ {
+ return this.Unauthorized();
+ }
+
+ if (!string.IsNullOrEmpty(result.Error))
+ {
+ // HACK: this will return the scheme with the error message as a challenge; non-standard method
+ return this.Unauthorized(new AuthenticationHeaderValue(JwtController.AuthScheme, result.Error));
+ }
+
+ return this.Ok(result);
+ }
+
+ ///
+ /// Represents the request data for a test POST.
+ ///
+ [JsonObject]
+ public class TestPostData
+ {
+ ///
+ /// The text used in the test.
+ ///
+ [JsonProperty("text")]
+ public string Text;
+ }
+ }
+}
diff --git a/DNN Platform/Dnn.AuthServices.Jwt/Services/ServiceRouteMapper.cs b/DNN Platform/Dnn.AuthServices.Jwt/Services/ServiceRouteMapper.cs
index d94d2b82398..adf5959e8ca 100644
--- a/DNN Platform/Dnn.AuthServices.Jwt/Services/ServiceRouteMapper.cs
+++ b/DNN Platform/Dnn.AuthServices.Jwt/Services/ServiceRouteMapper.cs
@@ -2,16 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
-namespace Dnn.AuthServices.Jwt.Services
-{
- using DotNetNuke.Web.Api;
+namespace Dnn.AuthServices.Jwt.Services
+{
+ using DotNetNuke.Web.Api;
- public class ServiceRouteMapper : IServiceRouteMapper
- {
- public void RegisterRoutes(IMapRoute mapRouteManager)
- {
- mapRouteManager.MapHttpRoute(
- "JwtAuth", "default", "{controller}/{action}", new[] { this.GetType().Namespace });
- }
- }
-}
+ ///
+ /// Registers the API routes for this extension.
+ ///
+ public class ServiceRouteMapper : IServiceRouteMapper
+ {
+ ///
+ public void RegisterRoutes(IMapRoute mapRouteManager)
+ {
+ mapRouteManager.MapHttpRoute("JwtAuth", "default", "{controller}/{action}", new[] { this.GetType().Namespace });
+ }
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Web/Api/Auth/AuthMessageHandlerBase.cs b/DNN Platform/DotNetNuke.Web/Api/Auth/AuthMessageHandlerBase.cs
index 4abc246d033..c942c2e5c42 100644
--- a/DNN Platform/DotNetNuke.Web/Api/Auth/AuthMessageHandlerBase.cs
+++ b/DNN Platform/DotNetNuke.Web/Api/Auth/AuthMessageHandlerBase.cs
@@ -14,29 +14,49 @@ namespace DotNetNuke.Web.Api.Auth
using DotNetNuke.Instrumentation;
+ ///
+ /// Base class for authentication providers message handlers.
+ ///
public abstract class AuthMessageHandlerBase : DelegatingHandler
{
- private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(AuthMessageHandlerBase));
-
+ private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(AuthMessageHandlerBase));
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A value indicating whether this handler should be included by default in all API endpoints.
+ /// A value indicating whether this handler should enforce SSL usage.
protected AuthMessageHandlerBase(bool includeByDefault, bool forceSsl)
{
this.DefaultInclude = includeByDefault;
this.ForceSsl = forceSsl;
}
-
+
+ ///
+ /// Gets the name of the authentication scheme.
+ ///
public abstract string AuthScheme { get; }
-
+
+ ///
+ /// Gets a value indicating whether this handler should bypass the anti-forgery token check.
+ ///
public virtual bool BypassAntiForgeryToken => false;
-
+
+ ///
+ /// Gets a value indicating whether this handler should be included by default on all API endpoints.
+ ///
public bool DefaultInclude { get; }
-
+
+ ///
+ /// Gets a value indicating whether this handler should enforce SSL usage on it's endpoints.
+ ///
public bool ForceSsl { get; }
///
/// A chance to process inbound requests.
///
- /// the request message.
- /// a cancellationtoken.
+ /// The request message.
+ /// A cancellationtoken.
/// null normally, if a response is returned all inbound processing is terminated and the resposne is returned.
public virtual HttpResponseMessage OnInboundRequest(HttpRequestMessage request, CancellationToken cancellationToken)
{
@@ -47,13 +67,18 @@ public virtual HttpResponseMessage OnInboundRequest(HttpRequestMessage request,
/// A change to process outbound responses.
///
/// The response message.
- /// a cancellationtoken.
- /// the responsemessage.
+ /// A cancellationtoken.
+ /// The responsemessage.
public virtual HttpResponseMessage OnOutboundResponse(HttpResponseMessage response, CancellationToken cancellationToken)
{
return response;
}
-
+
+ ///
+ /// Checks if the current request is an XmlHttpRequest.
+ ///
+ /// The HTTP Request.
+ /// A value indicating whether the request is an XmlHttpRequest.
protected static bool IsXmlHttpRequest(HttpRequestMessage request)
{
string value = null;
@@ -66,13 +91,24 @@ protected static bool IsXmlHttpRequest(HttpRequestMessage request)
return !string.IsNullOrEmpty(value) &&
value.Equals("XmlHttpRequest", StringComparison.InvariantCultureIgnoreCase);
}
-
+
+ ///
+ /// Sets the current principal for the request.
+ ///
+ /// The principal to set.
+ /// The current request.
protected static void SetCurrentPrincipal(IPrincipal principal, HttpRequestMessage request)
{
Thread.CurrentPrincipal = principal;
request.GetHttpContext().User = principal;
}
-
+
+ ///
+ /// Asynchronously sends a response.
+ ///
+ /// The current request.
+ /// A cancellation token.
+ /// An HttpResponseMessage Task.
protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = this.OnInboundRequest(request, cancellationToken);
@@ -84,7 +120,12 @@ protected override Task SendAsync(HttpRequestMessage reques
return base.SendAsync(request, cancellationToken).ContinueWith(x => this.OnOutboundResponse(x.Result, cancellationToken), cancellationToken);
}
-
+
+ ///
+ /// Checks if the current request requires authentication.
+ ///
+ /// The current request.
+ /// A value indication whether the current request needs authentication.
protected bool NeedsAuthentication(HttpRequestMessage request)
{
if (this.MustEnforceSslInRequest(request))