Example #1
0
        /// <summary>
        /// 验证身份 验证签名的有效性
        /// </summary>
        /// <param name="encodeJwt"></param>
        /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
        public bool ValiToken(string encodeJwt, Func <Dictionary <string, string>, bool> validatePayLoad = null)
        {
            var success = true;
            var jwtArr  = encodeJwt.Split('.');

            if (jwtArr.Length < 3)//数据格式都不对直接pass
            {
                return(false);
            }
            var header  = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[0]));
            var payLoad = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[1]));
            //配置文件中取出来的签名秘钥
            var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));

            //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
            success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
            if (!success)
            {
                return(success);//签名不正确直接返回
            }

            //其次验证是否在有效期内(也应该必须)
            var now = ToUnixEpochDate(DateTime.UtcNow);

            success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));

            //不需要自定义验证不传或者传递null即可
            if (validatePayLoad == null)
            {
                return(true);
            }

            //再其次 进行自定义的验证
            success = success && validatePayLoad(payLoad);

            return(success);
        }
Example #2
0
        public bool ValidToken(string token, out Dictionary <string, string> info)
        {
            info = null;
            bool success = true;

            if (!token.Contains("."))
            {
                return(false);
            }

            var jwtArr = token.Split(".");

            if (jwtArr.Length < 3)
            {
                return(false);
            }

            string headerStr = jwtArr[0].Replace("Bearer ", "");

            var header  = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(headerStr));
            var payLoad = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[1]));
            var hs256   = new HMACSHA256(Encoding.ASCII.GetBytes(_jwtConfig.Value.IssuerSigningKey));

            success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(headerStr, ".", jwtArr[1])))));
            if (!success)
            {
                return(success);
            }

            var now = ToUnixEpochDate(DateTime.UtcNow);

            success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));

            info = payLoad;

            return(success);
        }
Example #3
0
        /// <summary>
        /// This method is used to convert the plain text to Encrypted/Un-Readable Text format.
        /// </summary>
        /// <param name="PlainText">Plain Text to Encrypt before transferring over the network.</param>
        /// <returns>Cipher Text</returns>
        public static string EncryptPlainTextToCipherText(string PlainText)
        {
            //Getting the bytes of Input String.
            byte[] toEncryptedArray = UTF8Encoding.UTF8.GetBytes(PlainText);

            MD5CryptoServiceProvider objMD5CryptoService = new MD5CryptoServiceProvider();

            //Gettting the bytes from the Security Key and Passing it to compute the Corresponding Hash Value.
            byte[] securityKeyArray = objMD5CryptoService.ComputeHash(UTF8Encoding.UTF8.GetBytes(_securityKey));

            //De-allocatinng the memory after doing the Job.
            objMD5CryptoService.Clear();

            var objTripleDESCryptoService = new TripleDESCryptoServiceProvider();

            //Assigning the Security key to the TripleDES Service Provider.
            objTripleDESCryptoService.Key = securityKeyArray;

            //Mode of the Crypto service is Electronic Code Book.
            objTripleDESCryptoService.Mode = CipherMode.ECB;

            //Padding Mode is PKCS7 if there is any extra byte is added.
            objTripleDESCryptoService.Padding = PaddingMode.PKCS7;

            var objCrytpoTransform = objTripleDESCryptoService.CreateEncryptor();

            //Transform the bytes array to resultArray
            byte[] resultArray = objCrytpoTransform.TransformFinalBlock(toEncryptedArray, 0, toEncryptedArray.Length);

            //Releasing the Memory Occupied by TripleDES Service Provider for Encryption.
            objTripleDESCryptoService.Clear();

            //Convert and return the encrypted data/byte into string format.
            var result = Convert.ToBase64String(resultArray, 0, resultArray.Length);

            return(Base64UrlEncoder.Encode(result));
        }
        /// <summary>
        /// 校验
        /// </summary>
        /// <param name="encodeJwt">加密后的Jwt令牌</param>
        /// <param name="options">Jwt选项配置</param>
        /// <param name="validatePayload">校验负载</param>
        public Code Validate(string encodeJwt, JwtOptions options, Func <IDictionary <string, string>, JwtOptions, Code> validatePayload)
        {
            if (string.IsNullOrWhiteSpace(options.Secret))
            {
                throw new ArgumentNullException(nameof(options.Secret),
                                                $@"{nameof(options.Secret)}为Null或空字符串。请在""appsettings.json""配置""{nameof(JwtOptions)}""节点及其子节点""{nameof(JwtOptions.Secret)}""");
            }
            var jwtArray = encodeJwt.Split('.');

            if (jwtArray.Length < 3)
            {
                return(Code.HttpRequestError);
            }
            var header  = JsonUtil.ToObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArray[0]));
            var payload = JsonUtil.ToObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArray[1]));

            // 首先验证签名是否正确
            var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(options.Secret));
            var sign  = Base64UrlEncoder.Encode(
                hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArray[0], ".", jwtArray[1]))));

            // 签名不正确直接返回
            if (!string.Equals(jwtArray[2], sign))
            {
                return(Code.HttpRequestError);
            }
            // 其次验证是否在有效期内
            var now = ToUnixEpochDate(DateTime.UtcNow);

            if (!(now >= long.Parse(payload["nbf"].ToString()) && now < long.Parse(payload["exp"].ToString())))
            {
                return(Code.TokenInvalid);
            }
            // 进行自定义验证
            return(validatePayload(payload, options));
        }
Example #5
0
        public bool Validate(string encodeJwt)
        {
            bool success = true;

            try
            {
                string[] JWTarr = encodeJwt.Split('.');
                Dictionary <string, object> header  = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(JWTarr[0]));
                Dictionary <string, object> payLoad = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(JWTarr[1]));
                var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey));
                success = success && string.Equals(JWTarr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(JWTarr[0], ".", JWTarr[1])))));
                if (!success)
                {
                    return(success);
                }
                var now = ToUnixEpochDate(DateTime.UtcNow);
                success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
            }
            catch
            {
                return(false);
            }
            return(success);
        }
        public static JsonWebKeySet GetJwks()
        {
            KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
                                                                   (authority, resource, scope) => KeyVaultUtil.GetToken(authority, resource, scope)));

            var key = Task.Run(() => keyVaultClient.GetKeyAsync(SignKey())).ConfigureAwait(false).GetAwaiter().GetResult();

            var e = Base64UrlEncoder.Encode(key.Key.E);
            var n = Base64UrlEncoder.Encode(key.Key.E);

            var jsonWebKey = new Microsoft.IdentityModel.Tokens.JsonWebKey()
            {
                Kid = KEY,
                Kty = "RSA",
                E   = Base64UrlEncoder.Encode(key.Key.E),
                N   = Base64UrlEncoder.Encode(key.Key.N),
                Alg = "RS256"
            };
            JsonWebKeySet jsonWebKeySet = new JsonWebKeySet();

            jsonWebKeySet.Keys.Add(jsonWebKey);

            return(jsonWebKeySet);
        }
Example #7
0
        public async Task <ActionResult> LoginExt(String token)
        {
            try
            {
                var decoded   = Base64UrlEncoder.DecodeBytes(token);
                var dp        = _dataProtectionProvider.Create("ExternalLogin");
                var decBytes  = dp.Unprotect(decoded);
                var strResult = Encoding.UTF8.GetString(decBytes);
                var xdata     = strResult.Split('\b');
                if (xdata.Length != 2)
                {
                    return(new HttpStatusCodeResult(400));
                }
                var user = await UserManager.FindAsync(new UserLoginInfo("ExternalId", xdata[0].ToUpperInvariant()));

                if (user == null)
                {
                    return(new HttpStatusCodeResult(400));
                }
                if (!DateTime.TryParseExact(xdata[1], "o", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out DateTime date))
                {
                    return(new HttpStatusCodeResult(400));
                }
                if ((DateTime.UtcNow - date).TotalSeconds > 300)                 // 5 minutes
                {
                    return(new HttpStatusCodeResult(400));
                }
                await SignInManager.SignInAsync(user, true, true);

                return(RedirectToLocal("~/"));
            }
            catch (Exception ex)
            {
                return(new HttpStatusCodeResult(400));
            }
        }
Example #8
0
        /// <inheritdoc/>
        public object Create(string algorithm, params object[] args)
        {
            if (IsSupportedAlgorithm(algorithm, args))
            {
                if (algorithm.Equals("EdDSA", StringComparison.OrdinalIgnoreCase))
                {
                    var keyMaterial = args[0] as JsonWebKey;
                    if (keyMaterial != null)
                    {
                        //TODO: Probably should check a case where both are defined (or some other combinations).
                        AsymmetricKeyParameter?keyParameter = null;
                        if (keyMaterial.X != null)
                        {
                            var decodedPublicBytes = Base64UrlEncoder.DecodeBytes(keyMaterial.X);
                            keyParameter = new Ed25519PublicKeyParameters(decodedPublicBytes, 0);
                        }
                        else if (keyMaterial.D != null)
                        {
                            var decodedPrivateBytes = Base64UrlEncoder.DecodeBytes(keyMaterial.D);
                            keyParameter = new Ed25519PrivateKeyParameters(decodedPrivateBytes, 0);
                        }
                        else
                        {
                            throw new ArgumentException("Key material needs to be provided");
                        }

                        var securityKey = new BouncyCastleEdDsaSecurityKey(keyParameter, keyMaterial.Crv, this);
                        return(new BouncyCastleEdDsaSignatureProvider(securityKey, algorithm));
                    }

                    throw new ArgumentException($"The key material argument in position args[0] expected is \"{typeof(JsonWebKey)}\".");
                }
            }

            throw new NotSupportedException();
        }
Example #9
0
        /// <summary>
        /// 验证身份 验证签名的有效性,
        /// </summary>
        /// <param name="encodeJwt"></param>
        /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
        /// <returns></returns>
        public bool ValidateToken(string accessToken)
        {
            try
            {
                string[] tokenSplitArray = accessToken.Split('.');
                var      header          = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(tokenSplitArray[0]));
                var      payLoad         = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(tokenSplitArray[1]));
                var      hs256           = new HMACSHA256(Encoding.ASCII.GetBytes(_securityKey));

                //验证签名是否正确
                if (!tokenSplitArray[2].Equals(Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(tokenSplitArray[0], ".", tokenSplitArray[1]))))))
                {
                    return(false);
                }

                //验证是否在有效期内
                long totalSeconds = ToUnixEpochDate(DateTime.UtcNow);
                if (!(totalSeconds >= long.Parse(payLoad["nbf"].ToString()) && totalSeconds < long.Parse(payLoad["exp"].ToString())))
                {
                    return(false);
                }

                //验证发布者和受众是否一致,如果是在实际环境中,sso有多个系统需要登录,那么可以给每个系统分配一个audience,维护一个列表来校验
                if (!(payLoad["iss"].Equals(_issuer) && payLoad["aud"].Equals(_audience)))
                {
                    return(false);
                }

                return(true);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex.Message);
                return(false);
            }
        }
Example #10
0
        /// <summary>
        /// DecodeNewPayload method implementation
        /// </summary>
        public Dictionary <string, string> DecodeNewPayload(string rawBLOBJwt)
        {
            if (string.IsNullOrWhiteSpace(rawBLOBJwt))
            {
                throw new ArgumentNullException(nameof(rawBLOBJwt));
            }
            var jwtParts = rawBLOBJwt.Split('.');

            if (jwtParts.Length != 3)
            {
                throw new ArgumentException("The JWT does not have the 3 expected components");
            }

            var json    = Base64UrlEncoder.Decode(jwtParts[1]);
            var jObject = JObject.Parse(json);

            var parameters    = new Dictionary <string, string>();
            var versionnumber = ((string)jObject["no"] ?? "0");
            var nextupdate    = ((string)jObject["nextUpdate"] ?? "1970-01-01");

            parameters.Add("Number", versionnumber);
            parameters.Add("NextUpdate", nextupdate);
            return(parameters);
        }
Example #11
0
    public void AddSigningCredential_with_invalid_crv_value_should_throw_exception(string curveOid, string alg, string crv)
    {
        IServiceCollection     services = new ServiceCollection();
        IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services);

        var key = new ECDsaSecurityKey(ECDsa.Create(
                                           ECCurve.CreateFromOid(Oid.FromOidValue(curveOid, OidGroup.All))));
        var parameters = key.ECDsa.ExportParameters(true);

        var jsonWebKeyFromECDsa = new JsonWebKey()
        {
            Kty   = JsonWebAlgorithmsKeyTypes.EllipticCurve,
            Use   = "sig",
            Kid   = key.KeyId,
            KeyId = key.KeyId,
            X     = Base64UrlEncoder.Encode(parameters.Q.X),
            Y     = Base64UrlEncoder.Encode(parameters.Q.Y),
            D     = Base64UrlEncoder.Encode(parameters.D),
            Crv   = crv.Replace("-", string.Empty),
            Alg   = SecurityAlgorithms.EcdsaSha256
        };

        Assert.Throws <InvalidOperationException>(() => identityServerBuilder.AddSigningCredential(jsonWebKeyFromECDsa, alg));
    }
Example #12
0
        public IActionResult Login()
        {
            var stateBytes = new byte[16];

            RandomNumberGenerator.Create().GetBytes(stateBytes);
            var state = Base64UrlEncoder.Encode(stateBytes);

            Response.Cookies.Append("state", state, new CookieOptions
            {
                HttpOnly    = true,
                Expires     = DateTimeOffset.Now.AddMinutes(10),
                IsEssential = true
            });
            var deleteCookieOptions = new CookieOptions
            {
                Expires     = DateTimeOffset.Now.AddDays(-100),
                HttpOnly    = true,
                IsEssential = true
            };

            if (Request.Cookies.ContainsKey("key"))
            {
                Response.Cookies.Append("key", "", deleteCookieOptions);
            }
            if (Request.Cookies.ContainsKey("username"))
            {
                Response.Cookies.Append("username", "", deleteCookieOptions);
            }
            if (Request.Cookies.ContainsKey("avatar"))
            {
                Response.Cookies.Append("avatar", "", deleteCookieOptions);
            }
            var uri = discordHttp.GetRedirectUri(state);

            return(Redirect(uri));
        }
Example #13
0
        private async Task CreateSigningKey()
        {
            if (await _outbackDbContext.Secrets.AnyAsync())
            {
                return;
            }

            var secret = ECDsa.Create(ECCurve.NamedCurves.nistP256);

            var ecdSaKey = new ECDsaSecurityKey(secret)
            {
                KeyId = _randomStringGenerator.GetRandomString(20)
            };

            var publicKey = ecdSaKey.ECDsa.ExportParameters(true);

            var cryptographyParameters = new CryptographyParameters
            {
                EncodedD = Base64UrlEncoder.Encode(publicKey.D),
                EncodedX = Base64UrlEncoder.Encode(publicKey.Q.X),
                EncodedY = Base64UrlEncoder.Encode(publicKey.Q.Y),
                KeyId    = ecdSaKey.KeyId
            };

            var outbackSecret = new OutbackSecret
            {
                ActiveSigningKey          = true,
                CryptographyData          = JsonSerializer.Serialize(cryptographyParameters),
                Expires                   = DateTime.UtcNow.AddYears(100),
                PublicKeyCryptographyType = PublicKeyCryptographyType.EC_NistP256,
            };

            await _outbackDbContext.Secrets.AddAsync(outbackSecret);

            await _outbackDbContext.SaveChangesAsync();
        }
        public async Task InvokeCryptographyEndpointAsync_RsaSecurityKeysAreCorrectlyExposed()
        {
            // Arrange
            var parameters = new RSAParameters {
                D        = Convert.FromBase64String("Uj6NrYBnyddhlJefYEP2nleCntAKlWyIttJC4cJnNxNN+OT2fQXhpTXRwW4R5YIS3HDqK/Fg2yoYm+OTVntAAgRFKveRx/WKwFo6UpnJc5u3lElhFa7IfosO9qXjErpX9ruAVqipekDLwQ++KmVVdgH4PK/o//nEx5zklGCdlEJURZYJPs9/7g1cx3UwvPp8jM7LgZL5OZRNyI3Jz4efrwiI2/vd8P28lAbpv/Ao4NwUDq/WKEnZ8JYSjLEKnZCfbX1ZEwf0Ic48jEKHmi1WEwpru1fMPoYfakrsY/VEfatPiDs8a5HABP/KaXcM4AZsr7HbzqAaNycV2xgdZimGcQ=="),
                DP       = Convert.FromBase64String("hi1e+0eQ/iYrfT4zpZVbx3dyfA7Ch/aujMt6nGMF+1LGaut86vDHM2JI0Gc2BKc+uPEu2bNAorhSmuSyGpfGYl0MYFQoVF/jyiGpzYPmhYpL5yLuN9jWAqNwjfstuRDLU9zTEfZnr3OSN85rZcgT7NUxlY8im1Y2TWYxGiEXw9E="),
                DQ       = Convert.FromBase64String("laVNkWIbnSuGo7nAxyUSdL2sXU3GZWwItwzTG0IK/0woFjArtCxGgNXW+V+GhxT7iHGAVJJSBvJ65TXrUYuBmoWj2CsoUs2mzK8ax4zg3CXrU61esCsGUoS2owR4FXlhYPmoVnglGu89bH72eXKixZsuF7vKW19nG703BXYEaEU="),
                Exponent = Convert.FromBase64String("AQAB"),
                InverseQ = Convert.FromBase64String("dhzLDS4F5WYHX+vH4+uL3Ei/K5lxw2A/dBHGtbS2X54gm7vARl+FrptOFFwIjjmsLuTjttAq9K1EP/XZIq8bjW6dXJ/IytnobIPSFkclEeQlMi4/2VDMG5915J0DwnKO9M+B8F3JViUyMv0pvb+ub+HHDVFkIr7zooCmY25i77Q="),
                Modulus  = Convert.FromBase64String("kXv7Pxf6mSf7mu6mPAOAoKAXl5kU7Q3h9zevC5i4Mm5bMk17XCh7ZvVxDzGA+1JmyxOX6sw3gMUl31FtIFlDhis8VnXKAPn8i1zrmebq+7QKzpE2GpoIpXjXbkPaHG/DbC67M1bux7/dE7lSUSifHRRLsbMUC2D4UahJ6miH2iPFNFyoa6CLtwosD8tIJKwmZ9r9zfqc9BrVGu24lZySjTSRttpLaTkgkBjxHmYhinKNEtj9wUfi1S1wPJUvf+roc6o+7jeBBV3EXJCsb6XCCXI7/e3umWp19odeRShXLQNQbNuuVC7yre4iidUDrWJ1jiaB06svUG+fVEi4FCMvEQ=="),
                P        = Convert.FromBase64String("xQGczmp4qD7Sez/ZqgW+O4cciTHvSqJqJUSdDd2l1Pd/szQ8avvzorrbSWOIULyv6eJb32+HuyLgy6rTSJ6THFobAnUv4ZTR7EGK26AJmP/BhD+3G+n21+4fzfbAxpHihkCYmO8aEl8fm/r4qPVXmCzFoXDZLMNIxFsdEXiFRS0="),
                Q        = Convert.FromBase64String("vQy5C++AzF+TRh6qwbKzOqt87ZHEHidIAh6ivRNewjzIgCWXpseVl7DimY1YdViOnw1VI7xY+EyiyTanq5caTqqB3KcDm2t40bJfrZuUcn/5puRIh1bKNDwIMLsuNCrjHmDlNbocqpYMOh0Pgw7ARNbqrnPjWsYGJPuMNFpax/U=")
            };

            var server = CreateAuthorizationServer(options => {
                options.SigningCredentials.Clear();
                options.SigningCredentials.AddKey(new RsaSecurityKey(parameters));
            });

            var client = new OpenIdConnectClient(server.CreateClient());

            // Act
            var response = await client.GetAsync(CryptographyEndpoint);

            var key = response[OpenIdConnectConstants.Parameters.Keys].First;

            // Assert
            Assert.Null(key[JsonWebKeyParameterNames.D]);
            Assert.Null(key[JsonWebKeyParameterNames.DP]);
            Assert.Null(key[JsonWebKeyParameterNames.DQ]);
            Assert.Null(key[JsonWebKeyParameterNames.P]);
            Assert.Null(key[JsonWebKeyParameterNames.Q]);

            Assert.Equal(parameters.Exponent, Base64UrlEncoder.DecodeBytes((string)key[JsonWebKeyParameterNames.E]));
            Assert.Equal(parameters.Modulus, Base64UrlEncoder.DecodeBytes((string)key[JsonWebKeyParameterNames.N]));
        }
        /// <summary>
        /// Creates a JsonWebToken (JWS or JWE).
        /// </summary>
        /// <param name="payload">A JObject that represents the JWT token payload.</param>
        /// <param name="signingCredentials">Defines the security key and algorithm that will be used to sign the JWT.</param>
        /// <param name="encryptingCredentials">Defines the security key and algorithm that will be used to encrypt the JWT.</param>
        /// <returns>A JWT in compact serialization format.</returns>
        private string CreateJsonWebToken(JObject payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials)
        {
            if (payload == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(payload));
            }

            string rawHeader;

            if (!JsonWebTokenManager.KeyToHeaderCache.TryGetValue(JsonWebTokenManager.GetHeaderCacheKey(signingCredentials), out rawHeader))
            {
                var header = signingCredentials == null ? new JObject() : new JObject
                {
                    { JwtHeaderParameterNames.Alg, signingCredentials.Algorithm },
                    { JwtHeaderParameterNames.Kid, signingCredentials.Key.KeyId },
                    { JwtHeaderParameterNames.Typ, JwtConstants.HeaderType }
                };

                rawHeader = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(header.ToString(Newtonsoft.Json.Formatting.None)));
                JsonWebTokenManager.KeyToHeaderCache.TryAdd(JsonWebTokenManager.GetHeaderCacheKey(signingCredentials), rawHeader);
            }

            string rawPayload   = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payload.ToString(Newtonsoft.Json.Formatting.None)));
            string rawSignature = signingCredentials == null ? string.Empty : JwtTokenUtilities.CreateEncodedSignature(string.Concat(rawHeader, ".", rawPayload), signingCredentials);

            var rawData = rawHeader + "." + rawPayload + "." + rawSignature;

            if (encryptingCredentials != null)
            {
                return(EncryptToken(rawData, encryptingCredentials));
            }
            else
            {
                return(rawData);
            }
        }
        // This method helps to create the JWT.
        // I tried using a JWT library, but could not get it to work. That is why
        // this method replicates the funcionality to build a JWT token.
        // The important part to understand here is that the values that are added to the payload
        // will be received by Saasler and stored as properties of the user. Ex: name, plan_id, etc.
        public static string CreateJWT(
            string yourUserId, string yourUserEmail, string yourUserToken)
        {
            var header = new {
                alg = "HS256",
                //typ = "JWT" <- This is to commented so it generates a JWT similar to the one the Ruby library generates.
            };

            // The header of the JWT token.
            var headerPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header));

            // Defining the JWT token payload
            var iat_    = DateTime.Now;
            var exp_    = iat_.AddMinutes(2);
            var payload = new {
                iat = Math.Truncate(DateTimeToUnixTimestamp(iat_)),
                exp = Math.Truncate(DateTimeToUnixTimestamp(exp_)),
                // you can add other attributes here.
                // They will be used in the future to make filters.
                id    = yourUserId,
                email = yourUserEmail,
                token = yourUserToken,
            };

            var payloadPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payload));

            // This MUST be the JWT SECRET generated for your account.
            // This will allow Saasler to decode the JWT TOKEN
            var secret    = "SAASLER_JWT_SECRET";
            var sha256    = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
            var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes($"{headerPart}.{payloadPart}"));
            var hash      = Base64UrlEncoder.Encode(hashBytes);
            var jwt       = $"{headerPart}.{payloadPart}.{hash}";

            return(jwt);
        }
        protected override void OnInitialized()
        {
            string iss = "https://login.microsoftonline.com/common/v2.0";
            string aud = "6cb04018-a3f5-46a7-b995-940c78f5aef3";
            string preferred_username = configuration.GetSection("JWTSettings")["DefaultSubject"];
            string sub   = "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ";
            string name  = "John Doe";
            string ver   = "2.0";
            string nonce = "12345";
            string oid   = "00000000-0000-0000-66f3-3332eca7ea81";
            string tid   = "9122040d-6c67-4c5b-b112-36a304b66dad";
            string aio   = "Df2UVXL1ix!lMCWMSOJBcFatzcGfvFGhjKv8q5g0x732dR5MB5BisvGQO7YWByjd8iQDLq!eGbIDakyp5mnOrcdqHeYSnltepQmRp6AIZ8jY";

            string host = configuration.GetSection("JWTSettings")["HostEnvironment"];

            SigningCertThumbprint = configuration.GetSection("JWTSettings")["SigningCertThumbprint"];

            jwt = new B2EToken
            {
                Version            = ver,
                Audience           = aud,
                IssuedAt           = DateTime.UtcNow.ToString(),
                iat                = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(),
                NotBefore          = DateTime.UtcNow.ToString(),
                nbf                = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(),
                Expiration         = DateTime.UtcNow.AddMinutes(60).ToString(),
                exp                = DateTimeOffset.UtcNow.AddMinutes(60).ToUnixTimeSeconds().ToString(),
                Issuer             = iss,
                sub                = sub,
                name               = name,
                preferred_username = preferred_username,
                oid                = oid,
                tid                = tid,
                nonce              = nonce,
                aio                = aio,
            };

            //One way to handle Windows-based certs
            if (Environment.OSVersion.Platform.ToString() == "Win32NT")
            {
                SigningCredentials = new Lazy <X509SigningCredentials>(() =>
                {
                    X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
                    certStore.Open(OpenFlags.ReadOnly);
                    X509Certificate2Collection certCollection = certStore.Certificates.Find(
                        X509FindType.FindByThumbprint,
                        SigningCertThumbprint,
                        false);
                    // Get the first cert with the thumbprint
                    if (certCollection.Count > 0)
                    {
                        SigningCertHash = Base64UrlEncoder.Encode(certCollection[0].GetCertHash());
                        return(new X509SigningCredentials(certCollection[0]));
                    }

                    throw new Exception("Certificate not found");
                });
            }

            //And another way to handle Linux certs
            if (Environment.OSVersion.Platform.ToString() == "Unix")
            {
                var bytes = System.IO.File.ReadAllBytes($"/var/ssl/private/{SigningCertThumbprint}.p12");
                var cert  = new X509Certificate2(bytes);

                SigningCertHash = Base64UrlEncoder.Encode(cert.GetCertHash());

                SigningCredentials = new Lazy <X509SigningCredentials>(() =>
                {
                    if (cert != null)
                    {
                        return(new X509SigningCredentials(cert));
                    }

                    throw new Exception("Certificate not found");
                });
            }
        }
        public static SecurityKeyIdentifier GetKeyIdentifier(this SecurityKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            SecurityKeyIdentifier identifier  = null;
            X509Certificate2      certificate = null;

            var x509SecurityKey = key as X509SecurityKey;

            if (x509SecurityKey != null)
            {
                certificate = x509SecurityKey.Certificate;
            }

            var x509AsymmetricSecurityKey = key as X509AsymmetricSecurityKey;

            if (x509AsymmetricSecurityKey != null)
            {
                // The X.509 certificate is not directly accessible when using X509AsymmetricSecurityKey.
                // Reflection is the only way to get the certificate used to create the security key.
                var field = typeof(X509AsymmetricSecurityKey).GetField(
                    name: "certificate",
                    bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic);
                Debug.Assert(field != null, "The 'certificate' field shouldn't be missing.");

                certificate = (X509Certificate2)field.GetValue(x509AsymmetricSecurityKey);
            }

            if (certificate != null)
            {
                identifier = new SecurityKeyIdentifier {
                    new X509IssuerSerialKeyIdentifierClause(x509SecurityKey.Certificate),
                    new X509RawDataKeyIdentifierClause(x509SecurityKey.Certificate),
                    new X509ThumbprintKeyIdentifierClause(x509SecurityKey.Certificate),
                    new LocalIdKeyIdentifierClause(x509SecurityKey.Certificate.Thumbprint.ToUpperInvariant()),
                    new NamedKeySecurityKeyIdentifierClause(JwtHeaderParameterNames.X5t, x509SecurityKey.Certificate.Thumbprint.ToUpperInvariant())
                };
            }

            if (identifier == null)
            {
                // Create an empty security key identifier.
                identifier = new SecurityKeyIdentifier();

                var rsaSecurityKey = key as RsaSecurityKey;
                if (rsaSecurityKey != null)
                {
                    // Resolve the underlying algorithm from the security key.
                    var algorithm = (RSA)rsaSecurityKey.GetAsymmetricAlgorithm(
                        algorithm: SecurityAlgorithms.RsaSha256Signature,
                        requiresPrivateKey: false);

                    Debug.Assert(algorithm != null,
                                 "SecurityKey.GetAsymmetricAlgorithm() shouldn't return a null algorithm.");

                    // Export the RSA public key to extract a key identifier based on the modulus component.
                    var parameters = algorithm.ExportParameters(includePrivateParameters: false);

                    Debug.Assert(parameters.Modulus != null,
                                 "RSA.ExportParameters() shouldn't return a null modulus.");

                    // Only use the 40 first chars of the base64url-encoded modulus.
                    var kid = Base64UrlEncoder.Encode(parameters.Modulus);
                    kid = kid.Substring(0, Math.Min(kid.Length, 40)).ToUpperInvariant();

                    identifier.Add(new RsaKeyIdentifierClause(algorithm));
                    identifier.Add(new LocalIdKeyIdentifierClause(kid));
                    identifier.Add(new NamedKeySecurityKeyIdentifierClause(JwtHeaderParameterNames.Kid, kid));
                }
            }

            // Mark the security key identifier as read-only to
            // ensure it can't be altered during a request.
            identifier.MakeReadOnly();

            return(identifier);
        }
Example #19
0
        private StorageQueueMetricDefinition GenerateBogusAzureStorageQueueMetricDefinition()
        {
            var bogusAzureMetricConfiguration = GenerateBogusAzureMetricConfiguration();
            var bogusGenerator = new Faker <StorageQueueMetricDefinition>()
                                 .StrictMode(ensureRulesForAllProperties: true)
                                 .RuleFor(metricDefinition => metricDefinition.Name, faker => faker.Name.FirstName())
                                 .RuleFor(metricDefinition => metricDefinition.Description, faker => faker.Lorem.Sentence(wordCount: 6))
                                 .RuleFor(metricDefinition => metricDefinition.ResourceType, faker => ResourceType.StorageQueue)
                                 .RuleFor(metricDefinition => metricDefinition.AccountName, faker => faker.Name.LastName())
                                 .RuleFor(metricDefinition => metricDefinition.QueueName, faker => faker.Name.FirstName())
                                 .RuleFor(metricDefinition => metricDefinition.SasToken, faker => $"?sig={Base64UrlEncoder.Encode(faker.Lorem.Sentence(wordCount: 3))}")
                                 .RuleFor(metricDefinition => metricDefinition.AzureMetricConfiguration, faker => bogusAzureMetricConfiguration);

            return(bogusGenerator.Generate());
        }
Example #20
0
 public static string EncodeUrl(string textoBase64)
 {
     return(Base64UrlEncoder.Encode(textoBase64));
 }
Example #21
0
 public static string DecodeUrl(string codificadoUrlBase64)
 {
     return(Base64UrlEncoder.Decode(codificadoUrlBase64));
 }
Example #22
0
 /// <summary>
 /// converts encoded base64 string to plain text
 /// </summary>
 /// <returns>decoded plain text string</returns>
 public override string ToString()
 {
     return(Base64UrlEncoder.Decode(myBase64String.Reverse()));
 }
Example #23
0
        private async Task <bool> InvokeTokenEndpointAsync()
        {
            if (!string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
            {
                Options.Logger.LogError("The token request was rejected because an invalid " +
                                        "HTTP method was received: {Method}.", Request.Method);

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "A malformed token request has been received: make sure to use POST."
                }));
            }

            // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
            if (string.IsNullOrEmpty(Request.ContentType))
            {
                Options.Logger.LogError("The token request was rejected because the " +
                                        "mandatory 'Content-Type' header was missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "A malformed token request has been received: " +
                                       "the mandatory 'Content-Type' header was missing from the POST request."
                }));
            }

            // May have media/type; charset=utf-8, allow partial match.
            if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
            {
                Options.Logger.LogError("The token request was rejected because an invalid 'Content-Type' " +
                                        "header was received: {ContentType}.", Request.ContentType);

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "A malformed token request has been received: " +
                                       "the 'Content-Type' header contained an unexcepted value. " +
                                       "Make sure to use 'application/x-www-form-urlencoded'."
                }));
            }

            var request = new OpenIdConnectRequest(await Request.ReadFormAsync());

            // Note: set the message type before invoking the ExtractTokenRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.Token);

            // Store the token request in the OWIN context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractTokenRequestContext(Context, Options, request);
            await Options.Provider.ExtractTokenRequest(@event);

            if (@event.HandledResponse)
            {
                return(true);
            }

            else if (@event.Skipped)
            {
                return(false);
            }

            else if (@event.IsRejected)
            {
                Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}",
                                        /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                        /* Description: */ @event.ErrorDescription);

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            // Reject token requests missing the mandatory grant_type parameter.
            if (string.IsNullOrEmpty(request.GrantType))
            {
                Options.Logger.LogError("The token request was rejected because the grant type was missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'grant_type' parameter was missing.",
                }));
            }

            // Reject grant_type=authorization_code requests if the authorization endpoint is disabled.
            else if (request.IsAuthorizationCodeGrantType() && !Options.AuthorizationEndpointPath.HasValue)
            {
                Options.Logger.LogError("The token request was rejected because the authorization code grant was disabled.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
                    ErrorDescription = "The authorization code grant is not allowed by this authorization server."
                }));
            }

            // Reject grant_type=authorization_code requests missing the authorization code.
            // See https://tools.ietf.org/html/rfc6749#section-4.1.3
            else if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.Code))
            {
                Options.Logger.LogError("The token request was rejected because the authorization code was missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'code' parameter was missing."
                }));
            }

            // Reject grant_type=refresh_token requests missing the refresh token.
            // See https://tools.ietf.org/html/rfc6749#section-6
            else if (request.IsRefreshTokenGrantType() && string.IsNullOrEmpty(request.RefreshToken))
            {
                Options.Logger.LogError("The token request was rejected because the refresh token was missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'refresh_token' parameter was missing."
                }));
            }

            // Reject grant_type=password requests missing username or password.
            // See https://tools.ietf.org/html/rfc6749#section-4.3.2
            else if (request.IsPasswordGrantType() && (string.IsNullOrEmpty(request.Username) ||
                                                       string.IsNullOrEmpty(request.Password)))
            {
                Options.Logger.LogError("The token request was rejected because the resource owner credentials were missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'username' and/or 'password' parameters " +
                                       "was/were missing from the request message."
                }));
            }

            // When client_id and client_secret are both null, try to extract them from the Authorization header.
            // See http://tools.ietf.org/html/rfc6749#section-2.3.1 and
            // http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
            if (string.IsNullOrEmpty(request.ClientId) && string.IsNullOrEmpty(request.ClientSecret))
            {
                var header = Request.Headers.Get("Authorization");
                if (!string.IsNullOrEmpty(header) && header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
                {
                    try {
                        var value = header.Substring("Basic ".Length).Trim();
                        var data  = Encoding.UTF8.GetString(Convert.FromBase64String(value));

                        var index = data.IndexOf(':');
                        if (index >= 0)
                        {
                            request.ClientId     = data.Substring(0, index);
                            request.ClientSecret = data.Substring(index + 1);
                        }
                    }

                    catch (FormatException) { }
                    catch (ArgumentException) { }
                }
            }

            var context = new ValidateTokenRequestContext(Context, Options, request);
            await Options.Provider.ValidateTokenRequest(context);

            // If the validation context was set as fully validated,
            // mark the OpenID Connect request as confidential.
            if (context.IsValidated)
            {
                request.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel,
                                    OpenIdConnectConstants.ConfidentialityLevels.Private);
            }

            if (context.HandledResponse)
            {
                return(true);
            }

            else if (context.Skipped)
            {
                return(false);
            }

            else if (context.IsRejected)
            {
                Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}",
                                        /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                        /* Description: */ context.ErrorDescription);

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            // Reject grant_type=client_credentials requests if validation was skipped.
            else if (context.IsSkipped && request.IsClientCredentialsGrantType())
            {
                Options.Logger.LogError("The token request must be fully validated to use the client_credentials grant type.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "Client authentication is required when using client_credentials."
                }));
            }

            // Ensure that the client_id has been set from the ValidateTokenRequest event.
            else if (context.IsValidated && string.IsNullOrEmpty(request.ClientId))
            {
                Options.Logger.LogError("The token request was validated but the client_id was not set.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.ServerError,
                    ErrorDescription = "An internal server error occurred."
                }));
            }

            // At this stage, client_id cannot be null for grant_type=authorization_code requests,
            // as it must either be set in the ValidateTokenRequest notification
            // by the developer or manually flowed by non-confidential client applications.
            // See https://tools.ietf.org/html/rfc6749#section-4.1.3
            if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.ClientId))
            {
                Options.Logger.LogError("The token request was rejected because the mandatory 'client_id' was missing.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "client_id was missing from the token request"
                }));
            }

            AuthenticationTicket ticket = null;

            // See http://tools.ietf.org/html/rfc6749#section-4.1
            // and http://tools.ietf.org/html/rfc6749#section-4.1.3 (authorization code grant).
            // See http://tools.ietf.org/html/rfc6749#section-6 (refresh token grant).
            if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
            {
                ticket = request.IsAuthorizationCodeGrantType() ?
                         await DeserializeAuthorizationCodeAsync(request.Code, request) :
                         await DeserializeRefreshTokenAsync(request.RefreshToken, request);

                if (ticket == null)
                {
                    Options.Logger.LogError("The token request was rejected because the " +
                                            "authorization code or the refresh token was invalid.");

                    return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "Invalid ticket"
                    }));
                }

                // If the client was fully authenticated when retrieving its refresh token,
                // the current request must be rejected if client authentication was not enforced.
                if (request.IsRefreshTokenGrantType() && !context.IsValidated && ticket.IsConfidential())
                {
                    Options.Logger.LogError("The token request was rejected because client authentication " +
                                            "was required to use the confidential refresh token.");

                    return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "Client authentication is required to use this ticket"
                    }));
                }

                if (ticket.Properties.ExpiresUtc.HasValue &&
                    ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow)
                {
                    Options.Logger.LogError("The token request was rejected because the " +
                                            "authorization code or the refresh token was expired.");

                    return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "Expired ticket"
                    }));
                }

                // Note: presenters may be empty during a grant_type=refresh_token request if the refresh token
                // was issued to a public client but cannot be null for an authorization code grant request.
                var presenters = ticket.GetPresenters();
                if (request.IsAuthorizationCodeGrantType() && !presenters.Any())
                {
                    Options.Logger.LogError("The token request was rejected because the authorization " +
                                            "code didn't contain any valid presenter.");

                    return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.ServerError,
                        ErrorDescription = "An internal server error occurred."
                    }));
                }

                // Ensure the authorization code/refresh token was issued to the client application making the token request.
                // Note: when using the refresh token grant, client_id is optional but must validated if present.
                // As a consequence, this check doesn't depend on the actual status of client authentication.
                // See https://tools.ietf.org/html/rfc6749#section-6
                // and http://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken
                if (!string.IsNullOrEmpty(request.ClientId) && presenters.Any() &&
                    !presenters.Contains(request.ClientId, StringComparer.Ordinal))
                {
                    Options.Logger.LogError("The token request was rejected because the authorization " +
                                            "code was issued to a different client application.");

                    return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "Ticket does not contain matching client_id"
                    }));
                }

                // Validate the redirect_uri flowed by the client application during this token request.
                // Note: for pure OAuth2 requests, redirect_uri is only mandatory if the authorization request
                // contained an explicit redirect_uri. OpenID Connect requests MUST include a redirect_uri
                // but the specifications allow proceeding the token request without returning an error
                // if the authorization request didn't contain an explicit redirect_uri.
                // See https://tools.ietf.org/html/rfc6749#section-4.1.3
                // and http://openid.net/specs/openid-connect-core-1_0.html#TokenRequestValidation
                var address = ticket.GetProperty(OpenIdConnectConstants.Properties.RedirectUri);
                if (request.IsAuthorizationCodeGrantType() && !string.IsNullOrEmpty(address))
                {
                    ticket.SetProperty(OpenIdConnectConstants.Properties.RedirectUri, null);

                    if (string.IsNullOrEmpty(request.RedirectUri))
                    {
                        Options.Logger.LogError("The token request was rejected because the mandatory 'redirect_uri' " +
                                                "parameter was missing from the grant_type=authorization_code request.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "redirect_uri was missing from the token request"
                        }));
                    }

                    else if (!string.Equals(address, request.RedirectUri, StringComparison.Ordinal))
                    {
                        Options.Logger.LogError("The token request was rejected because the 'redirect_uri' " +
                                                "parameter didn't correspond to the expected value.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "Authorization code does not contain matching redirect_uri"
                        }));
                    }
                }

                // If a code challenge was initially sent in the authorization request and associated with the
                // code, validate the code verifier to ensure the token request is sent by a legit caller.
                var challenge = ticket.GetProperty(OpenIdConnectConstants.Properties.CodeChallenge);
                if (request.IsAuthorizationCodeGrantType() && !string.IsNullOrEmpty(challenge))
                {
                    ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallenge, null);

                    // Get the code verifier from the token request.
                    // If it cannot be found, return an invalid_grant error.
                    var verifier = request.CodeVerifier;
                    if (string.IsNullOrEmpty(verifier))
                    {
                        Options.Logger.LogError("The token request was rejected because the required 'code_verifier' " +
                                                "parameter was missing from the grant_type=authorization_code request.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "The required 'code_verifier' was missing from the token request."
                        }));
                    }

                    // Note: the code challenge method is always validated when receiving the authorization request.
                    var method = ticket.GetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod);
                    ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod, null);

                    Debug.Assert(string.IsNullOrEmpty(method) ||
                                 string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Plain, StringComparison.Ordinal) ||
                                 string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Sha256, StringComparison.Ordinal),
                                 "The specified code challenge method should be supported.");

                    // If the S256 challenge method was used, compute the hash corresponding to the code verifier.
                    if (string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Sha256, StringComparison.Ordinal))
                    {
                        using (var algorithm = SHA256.Create()) {
                            // Compute the SHA-256 hash of the code verifier and encode it using base64-url.
                            // See https://tools.ietf.org/html/rfc7636#section-4.6 for more information.
                            var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(request.CodeVerifier));

                            verifier = Base64UrlEncoder.Encode(hash);
                        }
                    }

                    // Compare the verifier and the code challenge: if the two don't match, return an error.
                    // Note: to prevent timing attacks, a time-constant comparer is always used.
                    if (!OpenIdConnectServerHelpers.AreEqual(verifier, challenge))
                    {
                        Options.Logger.LogError("The token request was rejected because the 'code_verifier' was invalid.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "The specified 'code_verifier' was invalid."
                        }));
                    }
                }

                if (request.IsRefreshTokenGrantType() && !string.IsNullOrEmpty(request.Resource))
                {
                    // When an explicit resource parameter has been included in the token request
                    // but was missing from the initial request, the request MUST be rejected.
                    var resources = ticket.GetResources();
                    if (!resources.Any())
                    {
                        Options.Logger.LogError("The token request was rejected because the 'resource' parameter was not allowed.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "Token request cannot contain a resource parameter " +
                                               "if the authorization request didn't contain one"
                        }));
                    }

                    // When an explicit resource parameter has been included in the token request,
                    // the authorization server MUST ensure that it doesn't contain resources
                    // that were not allowed during the initial authorization/token request.
                    else if (!new HashSet <string>(resources).IsSupersetOf(request.GetResources()))
                    {
                        Options.Logger.LogError("The token request was rejected because the 'resource' parameter was not valid.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "Token request doesn't contain a valid resource parameter"
                        }));
                    }
                }

                if (request.IsRefreshTokenGrantType() && !string.IsNullOrEmpty(request.Scope))
                {
                    // When an explicit scope parameter has been included in the token request
                    // but was missing from the initial request, the request MUST be rejected.
                    // See http://tools.ietf.org/html/rfc6749#section-6
                    var scopes = ticket.GetScopes();
                    if (!scopes.Any())
                    {
                        Options.Logger.LogError("The token request was rejected because the 'scope' parameter was not allowed.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "Token request cannot contain a scope parameter " +
                                               "if the authorization request didn't contain one"
                        }));
                    }

                    // When an explicit scope parameter has been included in the token request,
                    // the authorization server MUST ensure that it doesn't contain scopes
                    // that were not allowed during the initial authorization/token request.
                    // See https://tools.ietf.org/html/rfc6749#section-6
                    else if (!new HashSet <string>(scopes).IsSupersetOf(request.GetScopes()))
                    {
                        Options.Logger.LogError("The token request was rejected because the 'scope' parameter was not valid.");

                        return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                            Error = OpenIdConnectConstants.Errors.InvalidGrant,
                            ErrorDescription = "Token request doesn't contain a valid scope parameter"
                        }));
                    }
                }
            }

            var notification = new HandleTokenRequestContext(Context, Options, request, ticket);
            await Options.Provider.HandleTokenRequest(notification);

            if (notification.HandledResponse)
            {
                return(true);
            }

            else if (notification.Skipped)
            {
                return(false);
            }

            else if (notification.IsRejected)
            {
                Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}",
                                        /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidGrant,
                                        /* Description: */ notification.ErrorDescription);

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            // Flow the changes made to the ticket.
            ticket = notification.Ticket;

            // Ensure an authentication ticket has been provided or return
            // an error code indicating that the grant type is not supported.
            if (ticket == null)
            {
                Options.Logger.LogError("The token request was rejected because no authentication " +
                                        "ticket was returned by application code.");

                return(await SendTokenResponseAsync(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
                    ErrorDescription = "The specified grant_type parameter is not supported."
                }));
            }

            return(await HandleSignInAsync(ticket));
        }
Example #24
0
        public override PasetoSecurityToken Verify(PasetoToken token, IEnumerable <SecurityKey> signingKeys)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }
            if (signingKeys == null || !signingKeys.Any())
            {
                throw new ArgumentNullException(nameof(signingKeys));
            }

            var keys = signingKeys.OfType <EdDsaSecurityKey>().ToList();

            if (!keys.Any())
            {
                throw new SecurityTokenInvalidSigningKeyException($"PASETO v2 requires key of type {typeof(EdDsaSecurityKey)}");
            }

            if (token.Version != PasetoConstants.Versions.V2)
            {
                throw new ArgumentException("Invalid PASETO version");
            }
            if (token.Purpose != PasetoConstants.Purposes.Public)
            {
                throw new ArgumentException("Invalid PASETO purpose");
            }

            // decode payload
            var payload = Base64UrlEncoder.DecodeBytes(token.EncodedPayload);

            if (payload.Length < 64)
            {
                throw new SecurityTokenInvalidSignatureException("Payload does not contain signature");
            }

            // extract signature from payload (rightmost 64 bytes)
            var signature = new byte[64];

            Buffer.BlockCopy(payload, payload.Length - 64, signature, 0, 64);

            // decode payload JSON
            var message = new byte[payload.Length - 64];

            Buffer.BlockCopy(payload, 0, message, 0, payload.Length - 64);
            token.SetPayload(Encoding.UTF8.GetString(message));

            // pack
            var signedMessage = PreAuthEncode(new[]
            {
                Encoding.UTF8.GetBytes(PublicHeader),
                message,
                Base64UrlEncoder.DecodeBytes(token.EncodedFooter ?? string.Empty)
            });

            // verify signature using valid keys
            foreach (var publicKey in keys)
            {
                var signer = new Ed25519Signer();
                signer.Init(false, publicKey.KeyParameters);
                signer.BlockUpdate(signedMessage, 0, signedMessage.Length);

                var isValidSignature = signer.VerifySignature(signature);
                if (isValidSignature)
                {
                    return(new PasetoSecurityToken(token));
                }
            }

            throw new SecurityTokenInvalidSignatureException("Invalid PASETO signature");
        }
Example #25
0
        /// <summary>
        /// Adds a specific <see cref="SecurityKey"/> to sign tokens issued by the OpenID Connect server.
        /// </summary>
        /// <param name="credentials">The options used to configure the OpenID Connect server.</param>
        /// <param name="key">The key used to sign security tokens issued by the server.</param>
        /// <returns>The signing credentials.</returns>
        public static IList <SigningCredentials> AddKey(
            [NotNull] this IList <SigningCredentials> credentials, [NotNull] SecurityKey key)
        {
            if (credentials == null)
            {
                throw new ArgumentNullException(nameof(credentials));
            }

            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256Signature))
            {
                var x509SecurityKey = key as X509SecurityKey;
                if (x509SecurityKey != null)
                {
                    return(credentials.AddCertificate(x509SecurityKey.Certificate));
                }

                var x509AsymmetricSecurityKey = key as X509AsymmetricSecurityKey;
                if (x509AsymmetricSecurityKey != null)
                {
                    // The X.509 certificate is not directly accessible when using X509AsymmetricSecurityKey.
                    // Reflection is the only way to get the certificate used to create the security key.
                    var field = typeof(X509AsymmetricSecurityKey).GetField(
                        name: "certificate",
                        bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic);
                    Debug.Assert(field != null);

                    return(credentials.AddCertificate((X509Certificate2)field.GetValue(x509AsymmetricSecurityKey)));
                }

                // Create an empty security key identifier.
                var identifier = new SecurityKeyIdentifier();

                var rsaSecurityKey = key as RsaSecurityKey;
                if (rsaSecurityKey != null)
                {
                    // Resolve the underlying algorithm from the security key.
                    var algorithm = (RSA)rsaSecurityKey.GetAsymmetricAlgorithm(
                        algorithm: SecurityAlgorithms.RsaSha256Signature,
                        requiresPrivateKey: false);
                    Debug.Assert(algorithm != null, "SecurityKey.GetAsymmetricAlgorithm() shouldn't return a null algorithm.");

                    // Export the RSA public key to extract a key identifier based on the modulus component.
                    var parameters = algorithm.ExportParameters(includePrivateParameters: false);
                    Debug.Assert(parameters.Modulus != null, "RSA.ExportParameters() shouldn't return a null modulus.");

                    // Only use the 40 first chars of the base64url-encoded modulus.
                    var kid = Base64UrlEncoder.Encode(parameters.Modulus);
                    kid = kid.Substring(0, Math.Min(kid.Length, 40)).ToUpperInvariant();

                    identifier.Add(new RsaKeyIdentifierClause(algorithm));
                    identifier.Add(new LocalIdKeyIdentifierClause(kid));
                }

                // Mark the security key identifier as read-only to
                // ensure it can't be altered during a request.
                identifier.MakeReadOnly();

                credentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature,
                                                       SecurityAlgorithms.Sha256Digest, identifier));

                return(credentials);
            }

            else if (key.IsSupportedAlgorithm(SecurityAlgorithms.HmacSha256Signature))
            {
                // When using an in-memory symmetric key, no identifier clause can be inferred from the key itself.
                // To prevent the built-in security token handlers from throwing an exception, a default identifier is added.
                var identifier = new SecurityKeyIdentifier(new LocalIdKeyIdentifierClause("Default"));

                // Mark the security key identifier as read-only to
                // ensure it can't be altered during a request.
                identifier.MakeReadOnly();

                credentials.Add(new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature,
                                                       SecurityAlgorithms.Sha256Digest, identifier));

                return(credentials);
            }

            throw new InvalidOperationException("A signature algorithm cannot be automatically inferred from the signing key. " +
                                                "Consider using 'options.SigningCredentials.Add(SigningCredentials)' instead.");
        }
Example #26
0
        private async Task <string> CreateTokenAsync(
            [NotNull] string type, [NotNull] AuthenticationTicket ticket,
            [NotNull] OpenIddictOptions options, [NotNull] HttpContext context,
            [NotNull] OpenIdConnectRequest request,
            [NotNull] ISecureDataFormat <AuthenticationTicket> format)
        {
            Debug.Assert(!(options.DisableTokenRevocation && options.UseReferenceTokens),
                         "Token revocation cannot be disabled when using reference tokens.");

            Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
                         type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                         type == OpenIdConnectConstants.TokenUsages.RefreshToken,
                         "Only authorization codes, access and refresh tokens should be created using this method.");

            // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed
            // and must exactly match the expiration date of the refresh token used in the token request.
            if (request.IsTokenRequest() && request.IsRefreshTokenGrantType() &&
                !options.UseSlidingExpiration && type == OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                var properties = request.GetProperty <AuthenticationTicket>(
                    OpenIddictConstants.Properties.AuthenticationTicket)?.Properties;
                Debug.Assert(properties != null, "The authentication properties shouldn't be null.");

                ticket.Properties.ExpiresUtc = properties.ExpiresUtc;
            }

            if (options.DisableTokenRevocation)
            {
                return(null);
            }

            var descriptor = new OpenIddictTokenDescriptor
            {
                AuthorizationId = ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId),
                CreationDate    = ticket.Properties.IssuedUtc,
                ExpirationDate  = ticket.Properties.ExpiresUtc,
                Principal       = ticket.Principal,
                Status          = OpenIddictConstants.Statuses.Valid,
                Subject         = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject),
                Type            = type
            };

            foreach (var property in ticket.Properties.Items)
            {
                descriptor.Properties.Add(property);
            }

            string result = null;

            // When reference tokens are enabled or when the token is an authorization code or a
            // refresh token, remove the unnecessary properties from the authentication ticket.
            if (options.UseReferenceTokens ||
                (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                 type == OpenIdConnectConstants.TokenUsages.RefreshToken))
            {
                ticket.Properties.IssuedUtc = ticket.Properties.ExpiresUtc = null;
                ticket.RemoveProperty(OpenIddictConstants.Properties.AuthorizationId)
                .RemoveProperty(OpenIdConnectConstants.Properties.TokenId);
            }

            // If reference tokens are enabled, create a new entry for
            // authorization codes, refresh tokens and access tokens.
            if (options.UseReferenceTokens)
            {
                // Note: the data format is automatically replaced at startup time to ensure
                // that encrypted tokens stored in the database cannot be considered as
                // valid tokens if the developer decides to disable reference tokens support.
                descriptor.Ciphertext = format.Protect(ticket);

                // Generate a new crypto-secure random identifier that will be
                // substituted to the ciphertext returned by the data format.
                var bytes = new byte[256 / 8];
                options.RandomNumberGenerator.GetBytes(bytes);
                result = Base64UrlEncoder.Encode(bytes);

                // Compute the digest of the generated identifier and use
                // it as the hashed identifier of the reference token.
                // Doing that prevents token identifiers stolen from
                // the database from being used as valid reference tokens.
                using (var algorithm = SHA256.Create())
                {
                    descriptor.Hash = Convert.ToBase64String(algorithm.ComputeHash(bytes));
                }
            }

            // Otherwise, only create a token metadata entry for authorization codes and refresh tokens.
            else if (type != OpenIdConnectConstants.TokenUsages.AuthorizationCode &&
                     type != OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                return(null);
            }

            // If the client application is known, associate it with the token.
            if (!string.IsNullOrEmpty(request.ClientId))
            {
                var application = await Applications.FindByClientIdAsync(request.ClientId, context.RequestAborted);

                if (application == null)
                {
                    throw new InvalidOperationException("The client application cannot be retrieved from the database.");
                }

                descriptor.ApplicationId = await Applications.GetIdAsync(application, context.RequestAborted);
            }

            // If a null value was returned by CreateAsync(), return immediately.
            var token = await Tokens.CreateAsync(descriptor, context.RequestAborted);

            if (token == null)
            {
                return(null);
            }

            // Throw an exception if the token identifier can't be resolved.
            var identifier = await Tokens.GetIdAsync(token, context.RequestAborted);

            if (string.IsNullOrEmpty(identifier))
            {
                throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
            }

            // Restore the token identifier using the unique
            // identifier attached with the database entry.
            ticket.SetTokenId(identifier);

            // Dynamically set the creation and expiration dates.
            ticket.Properties.IssuedUtc  = descriptor.CreationDate;
            ticket.Properties.ExpiresUtc = descriptor.ExpirationDate;

            // Restore the authorization identifier using the identifier attached with the database entry.
            ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId, descriptor.AuthorizationId);

            if (!string.IsNullOrEmpty(result))
            {
                Logger.LogTrace("A new reference token was successfully generated and persisted " +
                                "in the database: {Token} ; {Claims} ; {Properties}.",
                                result, ticket.Principal.Claims, ticket.Properties.Items);
            }

            return(result);
        }
Example #27
0
        private async Task <AuthenticationTicket> ReceiveTokenAsync(
            [NotNull] string type, [NotNull] string value,
            [NotNull] OpenIddictOptions options, [NotNull] HttpContext context,
            [NotNull] OpenIdConnectRequest request,
            [NotNull] ISecureDataFormat <AuthenticationTicket> format)
        {
            Debug.Assert(!(options.DisableTokenRevocation && options.UseReferenceTokens),
                         "Token revocation cannot be disabled when using reference tokens.");

            Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
                         type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                         type == OpenIdConnectConstants.TokenUsages.RefreshToken,
                         "Only authorization codes, access and refresh tokens should be validated using this method.");

            string identifier;
            AuthenticationTicket ticket;
            TToken token;

            if (options.UseReferenceTokens)
            {
                string hash;
                try
                {
                    // Compute the digest of the received token and use it
                    // to retrieve the reference token from the database.
                    using (var algorithm = SHA256.Create())
                    {
                        hash = Convert.ToBase64String(algorithm.ComputeHash(Base64UrlEncoder.DecodeBytes(value)));
                    }
                }

                // Swallow format-related exceptions to ensure badly formed
                // or tampered tokens don't cause an exception at this stage.
                catch
                {
                    return(null);
                }

                // Retrieve the token entry from the database. If it
                // cannot be found, assume the token is not valid.
                token = await Tokens.FindByHashAsync(hash, context.RequestAborted);

                if (token == null)
                {
                    Logger.LogInformation("The reference token corresponding to the '{Hash}' hashed " +
                                          "identifier cannot be found in the database.", hash);

                    return(null);
                }

                identifier = await Tokens.GetIdAsync(token, context.RequestAborted);

                if (string.IsNullOrEmpty(identifier))
                {
                    Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                      "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                // Extract the encrypted payload from the token. If it's null or empty,
                // assume the token is not a reference token and consider it as invalid.
                var ciphertext = await Tokens.GetCiphertextAsync(token, context.RequestAborted);

                if (string.IsNullOrEmpty(ciphertext))
                {
                    Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
                                      "This may indicate that the token is not a reference token.", identifier);

                    return(null);
                }

                ticket = format.Unprotect(ciphertext);
                if (ticket == null)
                {
                    Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +
                                      "This may indicate that the token entry is corrupted or tampered.",
                                      await Tokens.GetIdAsync(token, context.RequestAborted));

                    return(null);
                }
            }

            else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                     type == OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                ticket = format.Unprotect(value);
                if (ticket == null)
                {
                    Logger.LogTrace("The received token was invalid or malformed: {Token}.", value);

                    return(null);
                }

                // Retrieve the authorization code/refresh token entry from the database.
                // If it cannot be found, assume the authorization code/refresh token is not valid.
                token = await Tokens.FindByIdAsync(ticket.GetTokenId(), context.RequestAborted);

                if (token == null)
                {
                    Logger.LogInformation("The token '{Identifier}' cannot be found in the database.", ticket.GetTokenId());

                    return(null);
                }

                identifier = await Tokens.GetIdAsync(token, context.RequestAborted);

                if (string.IsNullOrEmpty(identifier))
                {
                    Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                      "This may indicate that the token entry is corrupted.");

                    return(null);
                }
            }

            else
            {
                return(null);
            }

            // Restore the token identifier using the unique
            // identifier attached with the database entry.
            ticket.SetTokenId(identifier);

            // Dynamically set the creation and expiration dates.
            ticket.Properties.IssuedUtc = await Tokens.GetCreationDateAsync(token, context.RequestAborted);

            ticket.Properties.ExpiresUtc = await Tokens.GetExpirationDateAsync(token, context.RequestAborted);

            // Restore the authorization identifier using the identifier attached with the database entry.
            ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId,
                               await Tokens.GetAuthorizationIdAsync(token, context.RequestAborted));

            Logger.LogTrace("The token '{Identifier}' was successfully decrypted and " +
                            "retrieved from the database: {Claims} ; {Properties}.",
                            ticket.GetTokenId(), ticket.Principal.Claims, ticket.Properties.Items);

            return(ticket);
        }
Example #28
0
        public async Task <IActionResult> GenerateToken([FromBody] TokenRequestModel model, CancellationToken cancellationToken, [FromFilter] byte[]?addressHash = default)
        {
            if (!TryValidateModel(model))
            {
                return(BadRequest(ModelState));
            }

            switch (model.Type)
            {
            case TokenRequestType.SignatureRequest:
            {
                if (addressHash == default)
                {
                    return(BadRequest());
                }

                if (string.IsNullOrWhiteSpace(_serverOptions.Value.ServerId))
                {
                    return(StatusCode((int)HttpStatusCode.NotImplemented));
                }

                return(Ok(new TokenResponseModel {
                        Token = Base64UrlEncoder.Encode(addressHash)
                    }));
            }
            }

            var query = new ResourceQuery();
            var user  = _users.Get(query, cancellationToken).SingleOrDefault();

            if (user == default)
            {
                return(Unauthorized());
            }

            switch (model.Type)
            {
            case TokenRequestType.UsernamePassword:
                break;

            case TokenRequestType.SignatureRequest:
            {
                Debug.Assert(addressHash != default);
                Debug.Assert(!string.IsNullOrWhiteSpace(_serverOptions.Value.ServerId));

                // Challenge = sha256(WyHash64(IP)[8]:ServerId[16]:Nonce[24])
                var buffer = Crypto.Nonce(48);

                for (var i = 0; i < _serverOptions.Value.ServerId.Length; i++)
                {
                    buffer[i] = (byte)_serverOptions.Value.ServerId[i];
                }

                for (var i = 8; i < addressHash.Length; i++)
                {
                    buffer[i] = addressHash[i];
                }

                break;
            }

            case TokenRequestType.SignatureResponse:
            {
                break;
            }

            case null:
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            var issuedAt  = _timestamps();
            var expiresAt = issuedAt + _tokenOptions.Value.Lifetime;

            var claims = new[]
            {
                // https://tools.ietf.org/html/rfc7519#section-4.1.2
                new Claim(JwtRegisteredClaimNames.Sub, model.Identity ?? string.Empty),

                // https://tools.ietf.org/html/rfc7519#section-4.1.4
                new Claim(JwtRegisteredClaimNames.Exp, expiresAt.ToUnixTimeSeconds().ToString()),

                // https://tools.ietf.org/html/rfc7519#section-4.1.6
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            };

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenOptions.Value?.SigningKey ?? throw new InvalidOperationException(
                                                                                  _localizer.GetString("Bearer authentication requires a valid signing key"))));

            var signing = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            string token;

            switch (_tokenOptions.Value.Format)
            {
            case TokenFormat.JsonWebToken:
                token = IssueJwtToken(claims, signing);
                break;

            case TokenFormat.JsonWebEncryption:
                var encrypting = await _encryptionKeyStore.GetCredentialsAsync();

                token = IssueJweToken(claims, signing, encrypting);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            var response = new TokenResponseModel {
                Token = token
            };

            return(Ok(response));
        }
Example #29
0
        public void EncodedLength_is_negative___throws_ArgumentOutOfRange(int encodedLength)
        {
            var sut = new Base64UrlEncoder();

            Assert.Throws <ArgumentOutOfRangeException>(() => sut.GetDecodedLength(encodedLength));
        }
Example #30
0
        public void EncodedLength_given_malformed___throws_MalformedInput(int encodedLength)
        {
            var sut = new Base64UrlEncoder();

            Assert.Throws <FormatException>(() => sut.GetDecodedLength(encodedLength));
        }