예제 #1
0
 private void SetInvalidRequest(OioIdwsMatchEndpointContext context, string message)
 {
     _logger.WriteWarning($"Returned invalid request: {message}");
     // Scheme is mandatory and Holder-Of-Key is currently the only supportede scheme at NemLog-in STS. Hence, we specify Holder-Of-Key.
     context.Response.SetAuthenticationFailed(AccessTokenType.HolderOfKey, AuthenticationErrorCodes.InvalidRequest, message);
     context.RequestCompleted();
 }
예제 #2
0
 private void RequestFailed(OioIdwsMatchEndpointContext context, int statusCode, string error, Exception ex = null, string accessToken = null)
 {
     _logger.WriteEntry(Log.RetrieveAccessTokenRequestFailed(error, accessToken, ex));
     context.Response.StatusCode = statusCode;
     context.Response.Write(new JObject(
                                new JProperty("error", error)).ToString());
     context.RequestCompleted();
 }
예제 #3
0
        private void TokenExpired(OioIdwsMatchEndpointContext context, string accessToken)
        {
            var reason = "The token has expired";

            _logger.WriteEntry(Log.TokenExpired(accessToken));
            context.Response.StatusCode = 401;
            context.Response.Write(new JObject(
                                       new JProperty("error", reason),
                                       new JProperty("expired", 1)).ToString());
            context.RequestCompleted();
        }
예제 #4
0
        private async Task <string> GenerateAccessTokenAsync(OioIdwsMatchEndpointContext context, OioIdwsToken storedToken)
        {
            var uniqueKey = _keyGenerator.GenerateUniqueKey();

            _logger.WriteEntry(Log.NewUniqueKeyGenerated(uniqueKey));

            await _securityTokenStore.StoreTokenAsync(uniqueKey, storedToken);

            _logger.WriteVerbose("Token information was committed to the Token Store");

            //store the Expiry time directly in the protected access token, allowing the Authorization Server to quickly validate the token when asked to retrieve information
            var properties = new AuthenticationProperties
            {
                ExpiresUtc = storedToken.ExpiresUtc
            };

            properties.Value(uniqueKey);

            var accessToken = context.Options.TokenDataFormat.Protect(properties);

            return(accessToken);
        }
예제 #5
0
        public async Task RetrieveAsync(OioIdwsMatchEndpointContext context)
        {
            var clientCertificate = context.ClientCertificate();

            if (clientCertificate?.Thumbprint == null ||
                context.Options.TrustedWspCertificateThumbprints == null ||
                !context.Options.TrustedWspCertificateThumbprints
                .Any(x => clientCertificate.Thumbprint.Equals(x, StringComparison.OrdinalIgnoreCase)))
            {
                RequestFailed(context, 401, "No trusted client certificate was provided");
                return;
            }

            context.Response.ContentType = "application/json; charset=UTF-8";

            if (!context.Request.QueryString.HasValue)
            {
                RequestFailed(context, 400, "access token must be given as the query string");
                return;
            }

            var accessToken = context.Request.QueryString.Value;

            _logger.WriteEntry(Log.ProcessingToken(accessToken));

            AuthenticationProperties tokenProperties;

            try
            {
                tokenProperties = context.Options.TokenDataFormat.Unprotect(accessToken);
            }
            catch (Exception ex)
            {
                RequestFailed(context, 401, "Token could not be read", ex);
                return;
            }

            if (tokenProperties == null)
            {
                RequestFailed(context, 401, "Token could not be unprotected");
                return;
            }

            //check the Expires that was stored inside the protected access token, saving a round trip to the SecurityTokenStore if the token is expired
            if (tokenProperties.ExpiresUtc.GetValueOrDefault() + context.Options.MaxClockSkew < context.Options.SystemClock.UtcNow)
            {
                TokenExpired(context, accessToken);
                return;
            }

            var tokenValue = tokenProperties.Value();

            _logger.WriteEntry(Log.AttemptRetrieveInformationForAccessToken(tokenValue));
            var token = await _securityTokenStore.RetrieveTokenAsync(tokenValue);

            if (token == null)
            {
                _logger.WriteEntry(Log.CouldNotRetrieveTokenInformationFromStore());
                context.Response.StatusCode = 404;
                return;
            }

            // we might as well check expiry against that stored along with the token information even that we know that OioIdwsToken from token store always have the same expiry time as the access token ... this is set set in the AccessTokenIssuer.IssueAsync().
            if (token.ExpiresUtc + context.Options.MaxClockSkew < context.Options.SystemClock.UtcNow)
            {
                TokenExpired(context, accessToken);
                return;
            }

            var serializer = new JsonSerializer();

            using (var writer = new StreamWriter(context.Response.Body))
            {
                serializer.Serialize(writer, token);
            }

            _logger.WriteEntry(Log.ProcessingTokenCompleted(accessToken));

            context.RequestCompleted();
        }
예제 #6
0
        public async Task IssueAsync(OioIdwsMatchEndpointContext context)
        {
            if (string.IsNullOrEmpty(context.Request.ContentType))
            {
                SetInvalidRequest(context, "No content type was specified");
                return;
            }

            var ct = new System.Net.Mime.ContentType(context.Request.ContentType);

            var validContentType = "application/x-www-form-urlencoded";

            if (!ct.MediaType.Equals(validContentType, StringComparison.InvariantCultureIgnoreCase))
            {
                SetInvalidRequest(context, $"Content type '{validContentType}' is required.");
                return;
            }

            var form = await context.Request.ReadFormAsync();

            var tokenValueBase64 = form["saml-token"];

            if (string.IsNullOrEmpty(tokenValueBase64))
            {
                SetInvalidRequest(context, "saml-token was missing");
                return;
            }

            string tokenValue;

            try
            {
                var bytes = Convert.FromBase64String(tokenValueBase64);
                using (var stream = new MemoryStream(bytes))
                {
                    using (var reader = new StreamReader(stream))
                    {
                        tokenValue = await reader.ReadToEndAsync();
                    }
                }
            }
            catch (Exception)
            {
                SetInvalidRequest(context, "saml-token must be in base64");
                return;
            }

            var clientCertificate = context.ClientCertificate();

            _logger.WriteEntry(Log.StartingTokenValidation());
            var samlTokenValidation = await _tokenValidator.ValidateTokenAsync(tokenValue, clientCertificate, context.Options);

            if (!samlTokenValidation.Success)
            {
                _logger.WriteEntry(Log.IssuingTokenDenied(samlTokenValidation.ErrorDescription, samlTokenValidation.ValidationException));

                // Scheme is mandatory and Holder-Of-Key is currently the only supportede scheme at NemLog-in STS. Hence, we specify Holder-Of-Key.
                context.Response.SetAuthenticationFailed(AccessTokenType.HolderOfKey, AuthenticationErrorCodes.InvalidToken, samlTokenValidation.ErrorDescription);
                context.RequestCompleted();
                return;
            }

            _logger.WriteEntry(Log.TokenValidationCompleted());

            var expiresIn = context.Options.AccessTokenExpiration;

            int requestedExpiration;

            if (int.TryParse(form["should-expire-in"], out requestedExpiration))
            {
                var tmp = TimeSpan.FromSeconds(requestedExpiration);

                if (tmp < expiresIn)
                {
                    //if the client wants a lower expiration, that's ok. Never to increase it.
                    expiresIn = tmp;
                }
            }

            var storedToken = new OioIdwsToken
            {
                CertificateThumbprint = samlTokenValidation.AccessTokenType == AccessTokenType.HolderOfKey
                    ? clientCertificate?.Thumbprint?.ToLowerInvariant()
                    : null,
                Type       = samlTokenValidation.AccessTokenType,
                ExpiresUtc = context.Options.SystemClock.UtcNow + expiresIn,
                Claims     = samlTokenValidation.ClaimsIdentity.Claims.Select(x => new OioIdwsClaim
                {
                    Type      = x.Type,
                    Value     = x.Value,
                    Issuer    = x.Issuer,
                    ValueType = x.ValueType
                }).ToList(),
            };

            var accessToken = await GenerateAccessTokenAsync(context, storedToken);

            await WriteAccessTokenAsync(context.Response, accessToken, samlTokenValidation.AccessTokenType, expiresIn);

            _logger.WriteEntry(Log.TokenIssuedWithExpiration(accessToken, expiresIn));

            context.RequestCompleted();
        }