public async Task HandleNotificationAsync(
            MessageReceivedNotification notification,
            CancellationToken cancellationToken)
        {
            using var statsScope = _dogStatsd.StartTimer("message_processing_ms");

            var message = notification.Message;
            var channel = notification.Message.Channel;
            var guild   = (channel as IGuildChannel)?.Guild;

            using var logScope = MessageLogMessages.BeginMessageNotificationScope(_logger, guild?.Id, message.Id, channel.Id);

            MessageLogMessages.MessageReceivedHandling(_logger);

            await TryTrackMessageAsync(
                guild,
                notification.Message,
                async (guildId) =>
            {
                MessageLogMessages.MessageRecordCreating(_logger);
                await _messageRepository.CreateAsync(new MessageCreationData()
                {
                    Id        = message.Id,
                    GuildId   = guildId,
                    ChannelId = channel.Id,
                    AuthorId  = message.Author.Id,
                    Timestamp = message.Timestamp
                });
                MessageLogMessages.MessageRecordCreated(_logger);
            },
                cancellationToken);

            MessageLogMessages.MessageReceivedHandled(_logger);
        }
Example #2
0
        public void Constructor_Always_PropertiesAreGiven()
        {
            var mockMessage = new Mock <ISocketMessage>();

            var uut = new MessageReceivedNotification(mockMessage.Object);

            uut.Message.ShouldBeSameAs(mockMessage.Object);
        }
Example #3
0
        private async Task OnMessageReceived(MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            _log.Debug("OnMessageReceived: \r\nRequestType={0}\r\nAuthenticationType={1}\r\nRedirectUri={2}\r\nCode={3}",
                       notification.ProtocolMessage.RequestType,
                       notification.Options.AuthenticationType,
                       notification.Options.RedirectUri,
                       notification.ProtocolMessage.Code);

            await Task.FromResult(0);
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_InviteLinkIsForMessageGuild_DoesNotDeleteMessage(string messageContent)
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, messageContent));

            await uut.HandleNotificationAsync(notification);

            autoMocker.MessageShouldNotHaveBeenDeleted();
        }
        private static Task HeaderReceived(MessageReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
        {
            List<Claim> claims =
                new List<Claim>
                {
                    new Claim(ClaimTypes.Email, "*****@*****.**"),
                    new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
                };

            notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims)), new Http.Authentication.AuthenticationProperties(), notification.Options.AuthenticationScheme);
            notification.HandleResponse();

            return Task.FromResult<object>(null);
        }
        private static Task HeaderReceived(MessageReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions> notification)
        {
            List <Claim> claims =
                new List <Claim>
            {
                new Claim(ClaimTypes.Email, "*****@*****.**"),
                new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
            };

            notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsIdentity(claims, notification.Options.AuthenticationType), new Http.Security.AuthenticationProperties());
            notification.HandleResponse();

            return(Task.FromResult <object>(null));
        }
        private async Task OnMessageReceived(MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var tokenResponse =
                TokenHelper.GetTokens(notification.ProtocolMessage.Code, notification.Options.RedirectUri);

            notification.ProtocolMessage.TokenType    = tokenResponse.TokenType;
            notification.ProtocolMessage.ExpiresIn    = tokenResponse.ExpiresIn.ToString();
            notification.ProtocolMessage.IdToken      = tokenResponse.IdentityToken;
            notification.ProtocolMessage.IdTokenHint  = tokenResponse.IdentityToken;
            notification.ProtocolMessage.AccessToken  = tokenResponse.AccessToken;
            notification.ProtocolMessage.RefreshToken = tokenResponse.RefreshToken;
            notification.ProtocolMessage.Code         = null; // This is required so that the AuthorizationCodeReceived event does not trigger after SecurityTokenValidated event

            await Task.FromResult(0);
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_MessageChannelIsNotGuildChannel_DoesNotDeleteMessage()
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, DefaultInviteLink));

            autoMocker.GetMock <ISocketMessage>()
            .Setup(x => x.Channel)
            .Returns(new Mock <IMessageChannel>().Object);

            await uut.HandleNotificationAsync(notification);

            autoMocker.MessageShouldNotHaveBeenDeleted();
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_MessageAuthorIsSelfUser_DoesNotDeleteMessage()
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, DefaultInviteLink));

            autoMocker.GetMock <ISocketSelfUser>()
            .Setup(x => x.Id)
            .Returns(autoMocker.Get <IGuildUser>().Id);

            await uut.HandleNotificationAsync(notification);

            autoMocker.MessageShouldNotHaveBeenDeleted();
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_Otherwise_DeletesMessage(string messageContent)
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, messageContent));

            await uut.HandleNotificationAsync(notification);

            autoMocker.GetMock <IModerationService>()
            .ShouldHaveReceived(x => x.
                                DeleteMessageAsync(
                                    notification.Message,
                                    It.Is <string>(y => y.Contains("invite", StringComparison.OrdinalIgnoreCase)),
                                    autoMocker.Get <ISocketSelfUser>().Id));
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_MessageAuthorHasPostInviteLink_DoesNotDeleteMessage()
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, DefaultInviteLink));

            autoMocker.GetMock <IAuthorizationService>()
            .Setup(x => x.HasClaimsAsync(
                       autoMocker.Get <IGuildUser>(),
                       AuthorizationClaim.PostInviteLink))
            .ReturnsAsync(true);

            await uut.HandleNotificationAsync(notification);

            autoMocker.MessageShouldNotHaveBeenDeleted();
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_Otherwise_SendsResponse()
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, DefaultInviteLink));

            await uut.HandleNotificationAsync(notification);

            autoMocker.GetMock <IMessageChannel>()
            .ShouldHaveReceived(x => x.
                                SendMessageAsync(
                                    It.Is <string>(y => y.Contains("invite", StringComparison.OrdinalIgnoreCase)),
                                    It.IsAny <bool>(),
                                    It.IsAny <Embed>(),
                                    It.IsAny <RequestOptions>()));
        }
        public async Task HandleNotificationAsync_MessageReceivedNotification_MessageChannelIsUnmoderated_DoesNotDeleteMessage()
        {
            (var autoMocker, var uut) = BuildTestContext();

            var notification = new MessageReceivedNotification(
                message: BuildTestMessage(autoMocker, DefaultInviteLink));

            autoMocker.GetMock <IDesignatedChannelService>()
            .Setup(x => x.ChannelHasDesignationAsync(
                       autoMocker.Get <IGuild>(),
                       autoMocker.Get <IMessageChannel>(),
                       DesignatedChannelType.Unmoderated))
            .ReturnsAsync(true);

            await uut.HandleNotificationAsync(notification);

            autoMocker.MessageShouldNotHaveBeenDeleted();
        }
Example #14
0
        public async Task Handle(MessageReceivedNotification notification, CancellationToken cancellationToken)
        {
            if (!(notification.Message.Channel is SocketGuildChannel channel))
            {
                return;
            }

            if (notification.Message.Channel.Id != (await _db.Guilds.FindAsync(channel.Guild.Id)).LolaBlessGame)
            {
                return;
            }

            var commands = new[] { "bless me, lola", "bless me!", "lola bless", "<:LolaBless:731455738152747108>" };

            if (!commands.Any(c => notification.Message.Content.StartsWith(c)))
            {
                return;
            }

            var blessing = _wheel.SelectRandom();

            if (blessing.Type == BlessingType.Kiss)
            {
                var embed = new EmbedBuilder()
                            .WithUserAsAuthor(Context.User)
                            .WithTitle("You have been kissed")
                            .WithDescription(blessing.Text)
                            .WithColor(0xFA877F)
                            .WithCurrentTimestamp();

                await notification.Message.Channel.SendMessageAsync(
                    notification.Message.Author.Mention,
                    embed : embed.Build());
            }
            else
            {
                await notification.Message.Channel.SendMessageAsync(
                    $"{notification.Message.Author.Mention} {blessing.Text}");
            }

            await AwardUserAsync(blessing, channel.Guild, (SocketGuildUser)notification.Message.Author);
        }
        private async Task <MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> > RunMessageReceivedNotificationAsync(OpenIdConnectMessage message)
        {
            Logger.LogDebug(Resources.OIDCH_0001_MessageReceived, message.BuildRedirectUrl());
            var messageReceivedNotification =
                new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
            {
                ProtocolMessage = message
            };

            await Options.Notifications.MessageReceived(messageReceivedNotification);

            if (messageReceivedNotification.HandledResponse)
            {
                Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
            }
            else if (messageReceivedNotification.Skipped)
            {
                Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
            }

            return(messageReceivedNotification);
        }
Example #16
0
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns></returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            WsFederationMessage wsFederationMessage = null;

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                !string.IsNullOrWhiteSpace(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    // Buffer in case this body was not meant for us.
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }
                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                // TODO: a delegate on WsFederationAuthenticationOptions would allow for users to hook their own custom message.
                wsFederationMessage = new WsFederationMessage(form);
            }

            if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage)
            {
                return(null);
            }

            ExceptionDispatchInfo authFailedEx = null;

            try
            {
                var messageReceivedNotification = new MessageReceivedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                if (wsFederationMessage.Wresult == null)
                {
                    _logger.WriteWarning("Received a sign-in message without a WResult.");
                    return(null);
                }

                string token = wsFederationMessage.GetToken();
                if (string.IsNullOrWhiteSpace(token))
                {
                    _logger.WriteWarning("Received a sign-in message without a token.");
                    return(null);
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled);
                }

                // Copy and augment to avoid cross request race conditions for updated configurations.
                TokenValidationParameters tvp     = Options.TokenValidationParameters.Clone();
                IEnumerable <string>      issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers      = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys));

                SecurityToken   parsedToken;
                ClaimsPrincipal principal      = Options.SecurityTokenHandlers.ValidateToken(token, tvp, out parsedToken);
                ClaimsIdentity  claimsIdentity = principal.Identity as ClaimsIdentity;

                // Retrieve our cached redirect uri
                string state = wsFederationMessage.Wctx;
                // WsFed allows for uninitiated logins, state may be missing.
                AuthenticationProperties properties = GetPropertiesFromWctx(state);
                AuthenticationTicket     ticket     = new AuthenticationTicket(claimsIdentity, properties);

                if (Options.UseTokenLifetime)
                {
                    // Override any session persistence to match the token lifetime.
                    DateTime issued = parsedToken.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        ticket.Properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    DateTime expires = parsedToken.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    ticket.Properties.AllowRefresh = false;
                }

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = wsFederationMessage,
                };

                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null);
                }
                // Flow possible changes
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                return(ticket);
            }
            catch (Exception exception)
            {
                // We can't await inside a catch block, capture and handle outside.
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = wsFederationMessage,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
 internal static async Task MessageReceived(MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
 {
     Helpers.ThrowIfConditionFailed(() => context.ProtocolMessage != null, "ProtocolMessage is null.");
     notificationsFired.Add(nameof(MessageReceived));
     await Task.FromResult(0);
 }
        /// <summary>
        /// Invoked to process incoming OpenIdConnect messages.
        /// </summary>
        /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path. Need to make this runtime configurable.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            OpenIdConnectMessage openIdConnectMessage = null;

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                !string.IsNullOrWhiteSpace(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                openIdConnectMessage = new OpenIdConnectMessage(form);
            }

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

            try
            {
                var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage
                };

                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(messageReceivedNotification.AuthenticationTicket);
                }

                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
                // should process.
                AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
                if (properties == null)
                {
                    _logger.LogWarning("The state field is missing or invalid.");
                    return(null);
                }

                // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
                {
                    throw new OpenIdConnectProtocolException(
                              string.Format(CultureInfo.InvariantCulture,
                                            openIdConnectMessage.Error,
                                            Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
                }

                // code is only accepted with id_token, in this version, hence check for code is inside this if
                // OpenIdConnect protocol allows a Code to be received without the id_token
                if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
                {
                    _logger.LogWarning("The id_token is missing.");
                    return(null);
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage
                };

                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(securityTokenReceivedNotification.AuthenticationTicket);
                }

                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                if (_configuration == null && Options.ConfigurationManager != null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
                }

                // Copy and augment to avoid cross request race conditions for updated configurations.
                TokenValidationParameters validationParameters = Options.TokenValidationParameters.Clone();
                if (_configuration != null)
                {
                    if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
                    {
                        validationParameters.ValidIssuer = _configuration.Issuer;
                    }
                    else if (!string.IsNullOrWhiteSpace(_configuration.Issuer))
                    {
                        validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? new[] { _configuration.Issuer } : validationParameters.ValidIssuers.Concat(new[] { _configuration.Issuer }));
                    }

                    validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
                }

                AuthenticationTicket ticket;
                SecurityToken        validatedToken = null;
                ClaimsPrincipal      principal      = null;
                JwtSecurityToken     jwt            = null;

                foreach (var validator in Options.SecurityTokenValidators)
                {
                    if (validator.CanReadToken(openIdConnectMessage.IdToken))
                    {
                        principal = validator.ValidateToken(openIdConnectMessage.IdToken, validationParameters, out validatedToken);
                        jwt       = validatedToken as JwtSecurityToken;
                        if (jwt == null)
                        {
                            throw new InvalidOperationException("Validated Security Token must be a JwtSecurityToken was: " + (validatedToken == null ? "null" : validatedToken.GetType().ToString()));
                        }
                    }
                }

                if (validatedToken == null)
                {
                    throw new InvalidOperationException("No SecurityTokenValidator found for token: " + openIdConnectMessage.IdToken);
                }

                ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
                }

                if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
                }

                if (Options.UseTokenLifetime)
                {
                    // Override any session persistence to match the token lifetime.
                    DateTime issued = validatedToken.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        ticket.Properties.IssuedUtc = issued;
                    }

                    DateTime expires = validatedToken.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        ticket.Properties.ExpiresUtc = expires;
                    }

                    ticket.Properties.AllowRefresh = false;
                }

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = openIdConnectMessage
                };

                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(securityTokenValidatedNotification.AuthenticationTicket);
                }

                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null);
                }

                var protocolValidationContext = new OpenIdConnectProtocolValidationContext
                {
                    AuthorizationCode = openIdConnectMessage.Code,
                    Nonce             = RetrieveNonce(jwt.Payload.Nonce),
                };

                Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
                if (openIdConnectMessage.Code != null)
                {
                    var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        Code             = openIdConnectMessage.Code,
                        JwtSecurityToken = jwt,
                        ProtocolMessage  = openIdConnectMessage,
                        RedirectUri      = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                                           ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
                    };

                    await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);

                    if (authorizationCodeReceivedNotification.HandledResponse)
                    {
                        return(authorizationCodeReceivedNotification.AuthenticationTicket);
                    }

                    if (authorizationCodeReceivedNotification.Skipped)
                    {
                        return(null);
                    }
                }

                return(ticket);
            }
            catch (Exception exception)
            {
                _logger.LogError("Exception occurred while processing message", exception);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                    Exception       = exception
                };

                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(authenticationFailedNotification.AuthenticationTicket);
                }

                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                throw;
            }
        }
    private async Task <AuthenticationTicket> CreateAuthenticationTicket(OpenIdConnectMessage openIdConnectMessage)
    {
        var messageReceivedNotification =
            new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
        {
            ProtocolMessage = openIdConnectMessage
        };
        await Options.Notifications.MessageReceived(messageReceivedNotification).ConfigureAwait(false);

        if (messageReceivedNotification.HandledResponse)
        {
            return(GetHandledResponseTicket());
        }
        if (messageReceivedNotification.Skipped)
        {
            return(null);
        }
        // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
        // should process.
        AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);

        if (properties == null)
        {
            _logger.WriteWarning("The state field is missing or invalid.");
            return(null);
        }
        // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
        if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
        {
            throw new OpenIdConnectProtocolException(
                      string.Format(CultureInfo.InvariantCulture,
                                    openIdConnectMessage.Error,
                                    "Exception_OpenIdConnectMessageError", openIdConnectMessage.ErrorDescription ?? string.Empty,
                                    openIdConnectMessage.ErrorUri ?? string.Empty));
        }
        // tokens.Item1 contains id token
        // tokens.Item2 contains access token
        Tuple <string, string> tokens = await GetTokens(openIdConnectMessage.Code, Options)
                                        .ConfigureAwait(false);

        if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
        {
            openIdConnectMessage.IdToken = tokens.Item1;
        }
        var securityTokenReceivedNotification =
            new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context,
                                                                                                             Options)
        {
            ProtocolMessage = openIdConnectMessage,
        };
        await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification).ConfigureAwait(false);

        if (securityTokenReceivedNotification.HandledResponse)
        {
            return(GetHandledResponseTicket());
        }
        if (securityTokenReceivedNotification.Skipped)
        {
            return(null);
        }
        if (_configuration == null)
        {
            _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled)
                             .ConfigureAwait(false);
        }
        // Copy and augment to avoid cross request race conditions for updated configurations.
        TokenValidationParameters tvp     = Options.TokenValidationParameters.Clone();
        IEnumerable <string>      issuers = new[] { _configuration.Issuer };

        tvp.ValidIssuers        = tvp.ValidIssuers?.Concat(issuers) ?? issuers;
        tvp.IssuerSigningTokens = tvp.IssuerSigningTokens?.Concat(_configuration.SigningTokens) ?? _configuration.SigningTokens;
        SecurityToken   validatedToken;
        ClaimsPrincipal principal =
            Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken);
        ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity;
        var            claims         = await GetClaims(tokens.Item2).ConfigureAwait(false);

        AddClaim(claims, claimsIdentity, "sub", ClaimTypes.NameIdentifier, Options.AuthenticationType);
        AddClaim(claims, claimsIdentity, "given_name", ClaimTypes.GivenName);
        AddClaim(claims, claimsIdentity, "family_name", ClaimTypes.Surname);
        AddClaim(claims, claimsIdentity, "preferred_username", ClaimTypes.Name);
        AddClaim(claims, claimsIdentity, "email", ClaimTypes.Email);
        // claims principal could have changed claim values, use bits received on wire for validation.
        JwtSecurityToken     jwt    = validatedToken as JwtSecurityToken;
        AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties);

        if (Options.ProtocolValidator.RequireNonce)
        {
            if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce))
            {
                openIdConnectMessage.Nonce = jwt.Payload.Nonce;
            }
            // deletes the nonce cookie
            RetrieveNonce(openIdConnectMessage);
        }
        // remember 'session_state' and 'check_session_iframe'
        if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
        {
            ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
        }
        if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
        {
            ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] =
                _configuration.CheckSessionIframe;
        }
        if (Options.UseTokenLifetime)
        {
            // Override any session persistence to match the token lifetime.
            DateTime issued = jwt.ValidFrom;
            if (issued != DateTime.MinValue)
            {
                ticket.Properties.IssuedUtc = issued.ToUniversalTime();
            }
            DateTime expires = jwt.ValidTo;
            if (expires != DateTime.MinValue)
            {
                ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
            }
            ticket.Properties.AllowRefresh = false;
        }
        var securityTokenValidatedNotification =
            new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context,
                                                                                                              Options)
        {
            AuthenticationTicket = ticket,
            ProtocolMessage      = openIdConnectMessage,
        };
        await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification).ConfigureAwait(false);

        if (securityTokenValidatedNotification.HandledResponse)
        {
            return(GetHandledResponseTicket());
        }
        if (securityTokenValidatedNotification.Skipped)
        {
            return(null);
        }
        // Flow possible changes
        ticket = securityTokenValidatedNotification.AuthenticationTicket;
        // there is no hash of the code (c_hash) in the jwt obtained from the server
        // I don't know how to perform the validation using ProtocolValidator without the hash
        // that is why the code below is commented
        //var protocolValidationContext = new OpenIdConnectProtocolValidationContext
        //{
        //    AuthorizationCode = openIdConnectMessage.Code,
        //    Nonce = nonce
        //};
        //Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
        if (openIdConnectMessage.Code != null)
        {
            var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
            {
                AuthenticationTicket = ticket,
                Code             = openIdConnectMessage.Code,
                JwtSecurityToken = jwt,
                ProtocolMessage  = openIdConnectMessage,
                RedirectUri      =
                    ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey)
                            ? ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey]
                            : string.Empty,
            };
            await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification)
            .ConfigureAwait(false);

            if (authorizationCodeReceivedNotification.HandledResponse)
            {
                return(GetHandledResponseTicket());
            }
            if (authorizationCodeReceivedNotification.Skipped)
            {
                return(null);
            }
            // Flow possible changes
            ticket = authorizationCodeReceivedNotification.AuthenticationTicket;
        }
        return(ticket);
    }
 private static Task MessageReceived(MessageReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
 {
     notification.Token = "CustomToken";
     return Task.FromResult<object>(null);
 }
Example #21
0
        /// <summary>
        /// Invokes the login procedure (2nd leg of SP-Initiated login). Analagous to Saml20SignonHandler from ASP.Net DLL
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task<AuthenticationTicket> Invoke(IOwinContext context)
        {
            Logger.Debug(TraceMessages.SignOnHandlerCalled);
            ExceptionDispatchInfo authFailedEx = null;
            try {
                var messageReceivedNotification = new MessageReceivedNotification<SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = new SamlMessage(context, configuration, null)
                };
                await options.Notifications.MessageReceived(messageReceivedNotification);
                if (messageReceivedNotification.HandledResponse) {
                    return null; // GetHandledResponseTicket()
                }
                if (messageReceivedNotification.Skipped) {
                    return null;
                }
                var requestParams = await HandleResponse(context);
                var assertion = context.Get<Saml20Assertion>("Saml2:assertion");
                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification<SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = new SamlMessage(context, configuration, assertion)
                };
                await options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
                if (securityTokenReceivedNotification.HandledResponse) {
                    return null; // GetHandledResponseTicket();
                }
                if (securityTokenReceivedNotification.Skipped) {
                    return null;
                }

                var ticket = await GetAuthenticationTicket(context, requestParams);

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification<SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage = new SamlMessage(context, configuration, assertion)
                };

                await options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
                if (securityTokenValidatedNotification.HandledResponse) {
                    return null; // GetHandledResponseTicket();
                }
                if (securityTokenValidatedNotification.Skipped) {
                    return null; // null;
                }
                // Flow possible changes
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(ticket.Identity, ticket.Properties);                
                return ticket;
            }
            catch (Exception ex) {
                authFailedEx = ExceptionDispatchInfo.Capture(ex);
            }
            if (authFailedEx != null) {
                Logger.Error("Exception occurred while processing message: " + authFailedEx.SourceException);
                var message = new SamlMessage(context, configuration, context.Get<Saml20Assertion>("Saml2:assertion"));
                var authenticationFailedNotification = new AuthenticationFailedNotification<SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = message,
                    Exception = authFailedEx.SourceException
                };
                await options.Notifications.AuthenticationFailed(authenticationFailedNotification);
                if (authenticationFailedNotification.HandledResponse) {
                    return null;//GetHandledResponseTicket();
                }
                if (authenticationFailedNotification.Skipped) {
                    return null; //null
                }

                authFailedEx.Throw();
            }
            return null;
        }
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != Request.Path)
            {
                return(null);
            }

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                !string.IsNullOrWhiteSpace(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    // Buffer in case this body was not meant for us.
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }
                IFormCollection form = await Request.ReadFormAsync();

                // Post preview release: a delegate on WsFederationAuthenticationOptions would allow for users to hook their own custom message.
                WsFederationMessage wsFederationMessage = new WsFederationMessage(form);
                if (!wsFederationMessage.IsSignInMessage)
                {
                    Request.Body.Seek(0, SeekOrigin.Begin);
                    return(null);
                }

                MessageReceivedNotification <WsFederationMessage> messageReceivedNotification = null;
                if (Options.Notifications != null && Options.Notifications.MessageReceived != null)
                {
                    messageReceivedNotification = new MessageReceivedNotification <WsFederationMessage> {
                        ProtocolMessage = wsFederationMessage
                    };
                    await Options.Notifications.MessageReceived(messageReceivedNotification);
                }

                if (messageReceivedNotification != null && messageReceivedNotification.Cancel)
                {
                    return(null);
                }

                if (wsFederationMessage.Wresult != null)
                {
                    string token = wsFederationMessage.GetToken();
                    if (Options.Notifications != null && Options.Notifications.SecurityTokenReceived != null)
                    {
                        SecurityTokenReceivedNotification securityTokenReceivedNotification = new SecurityTokenReceivedNotification {
                            SecurityToken = token
                        };
                        await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                        if (securityTokenReceivedNotification.Cancel)
                        {
                            return(null);
                        }
                    }

                    ExceptionDispatchInfo authFailedEx = null;
                    try
                    {
                        ClaimsPrincipal principal      = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters);
                        ClaimsIdentity  claimsIdentity = principal.Identity as ClaimsIdentity;

                        // Retrieve our cached redirect uri
                        string state = wsFederationMessage.Wctx;
                        AuthenticationProperties properties = GetPropertiesFromWctx(state);

                        AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties);

                        Request.Context.Authentication.SignIn(claimsIdentity);
                        if (Options.Notifications != null && Options.Notifications.SecurityTokenValidated != null)
                        {
                            await Options.Notifications.SecurityTokenValidated(new SecurityTokenValidatedNotification { AuthenticationTicket = ticket });
                        }

                        return(ticket);
                    }
                    catch (Exception exception)
                    {
                        // We can't await inside a catch block, capture and handle outside.
                        authFailedEx = ExceptionDispatchInfo.Capture(exception);
                    }

                    if (authFailedEx != null)
                    {
                        if (Options.Notifications != null && Options.Notifications.AuthenticationFailed != null)
                        {
                            // Post preview release: user can update metadata, need consistent messaging.
                            var authenticationFailedNotification = new AuthenticationFailedNotification <WsFederationMessage>()
                            {
                                ProtocolMessage = wsFederationMessage,
                                Exception       = authFailedEx.SourceException
                            };

                            await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                            if (!authenticationFailedNotification.Cancel)
                            {
                                authFailedEx.Throw();
                            }
                        }
                        else
                        {
                            authFailedEx.Throw();
                        }
                    }
                }
            }

            return(null);
        }
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            OpenIdConnectMessage openIdConnectMessage = null;

            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                !string.IsNullOrWhiteSpace(Request.ContentType) &&
                Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }

                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                openIdConnectMessage = new OpenIdConnectMessage(form);
            }

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

            ExceptionDispatchInfo authFailedEx = null;
            string policy = string.Empty;

            try
            {
                var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
                if (properties == null)
                {
                    _logger.WriteWarning("The state field is missing or invalid.");
                    return(null);
                }

                string nonce = null;
                if (Options.ProtocolValidator.RequireNonce)
                {
                    nonce = RetrieveNonce(openIdConnectMessage);
                }

                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
                {
                    throw new OpenIdConnectProtocolException(
                              string.Format(CultureInfo.InvariantCulture,
                                            openIdConnectMessage.Error,
                                            "", openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
                }

                if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
                {
                    _logger.WriteWarning("The id_token is missing.");
                    return(null);
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                // Enable Per-Policy Metadata Retreival
                if (properties.Dictionary.TryGetValue(PolicyParameter, out policy))
                {
                    B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager;
                    _configuration = await mgr.GetConfigurationAsync(Context.Request.CallCancelled, policy);
                }
                else
                {
                    _logger.WriteWarning("No policy identifier was found in the Authentication Properties of the request.");
                    return(null);
                }

                TokenValidationParameters tvp     = Options.TokenValidationParameters.Clone();
                IEnumerable <string>      issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers        = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningTokens = (tvp.IssuerSigningTokens == null ? _configuration.SigningTokens : tvp.IssuerSigningTokens.Concat(_configuration.SigningTokens));

                SecurityToken   validatedToken;
                ClaimsPrincipal principal      = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken);
                ClaimsIdentity  claimsIdentity = principal.Identity as ClaimsIdentity;

                JwtSecurityToken     jwt    = validatedToken as JwtSecurityToken;
                AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties);

                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
                }

                if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
                }

                if (Options.UseTokenLifetime)
                {
                    DateTime issued = jwt.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        ticket.Properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    DateTime expires = jwt.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    ticket.Properties.AllowRefresh = false;
                }

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null);
                }
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                var protocolValidationContext = new OpenIdConnectProtocolValidationContext
                {
                    AuthorizationCode = openIdConnectMessage.Code,
                    Nonce             = nonce,
                };

                Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
                if (openIdConnectMessage.Code != null)
                {
                    var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        Code             = openIdConnectMessage.Code,
                        JwtSecurityToken = jwt,
                        ProtocolMessage  = openIdConnectMessage,
                        RedirectUri      = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                                           ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
                    };
                    await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);

                    if (authorizationCodeReceivedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (authorizationCodeReceivedNotification.Skipped)
                    {
                        return(null);
                    }
                    ticket = authorizationCodeReceivedNotification.AuthenticationTicket;
                }

                return(ticket);
            }
            catch (Exception exception)
            {
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: '" + authFailedEx.ToString());

                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager;
                    mgr.RequestRefresh(policy);
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
Example #24
0
        public async Task HandleNotificationAsync(
            MessageReceivedNotification notification,
            CancellationToken cancellationToken)
        {
            var message = notification.Message;
            var channel = message.Channel;
            var guild   = (channel as IGuildChannel)?.Guild;
            var author  = message.Author;

            using var logScope = UserMetricsLogMessages.BeginMessageScope(_logger, guild?.Id, channel.Id, author.Id, message.Id);

            UserMetricsLogMessages.MessageReceivedHandling(_logger);

            if (guild is null)
            {
                UserMetricsLogMessages.IgnoringNonGuildMessage(_logger);
                return;
            }

            if (author.IsBot || author.IsWebhook)
            {
                UserMetricsLogMessages.IgnoringNonHumanMessage(_logger);
                return;
            }

            var isOnTopic = true;

            try
            {
                UserMetricsLogMessages.ChannelParticipationFetching(_logger);
                isOnTopic = await _designatedChannelService.ChannelHasDesignationAsync(
                    guild.Id,
                    channel.Id,
                    DesignatedChannelType.CountsTowardsParticipation,
                    cancellationToken);

                UserMetricsLogMessages.ChannelParticipationFetched(_logger, isOnTopic);
            }
            catch (Exception ex)
            {
                UserMetricsLogMessages.ChannelParticipationFetchFailed(_logger, ex);
            }

            var isRanked = false;

            try
            {
                UserMetricsLogMessages.UserRankFetching(_logger);
                isRanked = await _designatedRoleService.RolesHaveDesignationAsync(
                    guild.Id,
                    ((IGuildUser)author).RoleIds,
                    DesignatedRoleType.Rank,
                    cancellationToken);

                UserMetricsLogMessages.UserRankFetched(_logger, isRanked);
            }
            catch (Exception ex)
            {
                UserMetricsLogMessages.UserRankFetchFailed(_logger, ex);
            }

            var tags = new[]
            {
                $"guild:{guild.Name}",
                $"channel:{channel.Name}",
                $"offtopic:{!isOnTopic}",
                $"has_role:{isRanked}"
            };

            _dogStatsd.Increment(MessageReceivedCounterName, tags: tags);

            UserMetricsLogMessages.MessageReceivedHandled(_logger);
        }
Example #25
0
        /// <summary>
        /// Callback for LinphoneCoreListener
        /// </summary>
        public void MessageReceived(LinphoneChatMessage message)
        {
            if (BaseModel.UIDispatcher == null)
            {
                return;
            }
            BaseModel.UIDispatcher.BeginInvoke(() =>
            {
                LinphoneAddress fromAddress = message.From;
                string sipAddress           = String.Format("{0}@{1}", fromAddress.UserName, fromAddress.Domain);
                Logger.Msg("[LinphoneManager] Message received from " + sipAddress + ": " + message.Text + "\r\n");

                //Vibrate
                ChatSettingsManager settings = new ChatSettingsManager();
                settings.Load();
                if ((bool)settings.VibrateOnIncomingMessage)
                {
                    VibrationDevice vibrator = VibrationDevice.GetDefault();
                    vibrator.Vibrate(TimeSpan.FromSeconds(1));
                }

                if (MessageListener != null && MessageListener.GetSipAddressAssociatedWithDisplayConversation() != null && MessageListener.GetSipAddressAssociatedWithDisplayConversation().Equals(sipAddress))
                {
                    MessageListener.MessageReceived(message);
                }
                else
                {
                    DateTime date = new DateTime(message.Time * TimeSpan.TicksPerSecond, DateTimeKind.Utc).AddYears(1969).ToLocalTime();
                    DateTime now  = DateTime.Now;
                    string dateStr;
                    if (now.Year == date.Year && now.Month == date.Month && now.Day == date.Day)
                    {
                        dateStr = String.Format("{0:HH:mm}", date);
                    }
                    else if (now.Year == date.Year)
                    {
                        dateStr = String.Format("{0:ddd d MMM, HH:mm}", date);
                    }
                    else
                    {
                        dateStr = String.Format("{0:ddd d MMM yyyy, HH:mm}", date);
                    }

                    //TODO: Temp hack to remove
                    string url = message.ExternalBodyUrl;
                    url        = url.Replace("\"", "");

                    //Displays the message as a popup
                    if (MessageReceivedNotification != null)
                    {
                        MessageReceivedNotification.Dismiss();
                    }

                    MessageReceivedNotification = new CustomMessageBox()
                    {
                        Title              = dateStr,
                        Caption            = url.Length > 0 ? AppResources.ImageMessageReceived : AppResources.MessageReceived,
                        Message            = url.Length > 0 ? "" : message.Text,
                        LeftButtonContent  = AppResources.Close,
                        RightButtonContent = AppResources.Show
                    };

                    MessageReceivedNotification.Dismissed += (s, e) =>
                    {
                        switch (e.Result)
                        {
                        case CustomMessageBoxResult.RightButton:
                            BaseModel.CurrentPage.NavigationService.Navigate(new Uri("/Views/Chat.xaml?sip=" + Utils.ReplacePlusInUri(message.PeerAddress.AsStringUriOnly()), UriKind.RelativeOrAbsolute));
                            break;
                        }
                    };

                    MessageReceivedNotification.Show();
                }
            });
        }
Example #26
0
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns></returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path.
            //if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            //{
            //    return null;
            //}

            var SamlMessage = await SamlMessageFromRequest();

            if (SamlMessage == null || !SamlMessage.IsSignInMessage())
            {
                return(null);
            }

            ExceptionDispatchInfo authFailedEx = null;

            try {
                var messageReceivedNotification = new MessageReceivedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = SamlMessage
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                //if (SamlMessage.Wresult == null) {
                //    _logger.WriteWarning("Received a sign-in message without a WResult.");
                //    return null;
                //}

                string token = SamlMessage.GetToken();
                if (string.IsNullOrWhiteSpace(token))
                {
                    _logger.WriteWarning("Received a sign-in message without a token.");
                    return(null);
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = SamlMessage
                };
                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }


                // Copy and augment to avoid cross request race conditions for updated configurations.

                ClaimsPrincipal principal      = null;
                ClaimsIdentity  claimsIdentity = principal.Identity as ClaimsIdentity;

                // Retrieve our cached redirect uri
                // string state = SamlMessage.Wctx;
                // WsFed allows for uninitiated logins, state may be missing.
                AuthenticationProperties properties = null;// GetPropertiesFromWctx(state);
                AuthenticationTicket     ticket     = new AuthenticationTicket(claimsIdentity, properties);

                //if (Options.UseTokenLifetime) {
                //    // Override any session persistence to match the token lifetime.
                //    DateTime issued = parsedToken.ValidFrom;
                //    if (issued != DateTime.MinValue) {
                //        ticket.Properties.IssuedUtc = issued.ToUniversalTime();
                //    }
                //    DateTime expires = parsedToken.ValidTo;
                //    if (expires != DateTime.MinValue) {
                //        ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
                //    }
                //    ticket.Properties.AllowRefresh = false;
                //}

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = SamlMessage,
                };

                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null);
                }
                // Flow possible changes
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                return(ticket);
            }
            catch (Exception exception) {
                // We can't await inside a catch block, capture and handle outside.
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                //if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) {
                //    Options.ConfigurationManager.RequestRefresh();
                //}

                var authenticationFailedNotification = new AuthenticationFailedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = SamlMessage,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path. Need to make this runtime configurable.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            OpenIdConnectMessage openIdConnectMessage = null;

            if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase) &&
                Request.Query.Any(q => q.Key == "code"))
            {
                _logger.WriteVerbose("Processing request query string");
                openIdConnectMessage = new OpenIdConnectMessage(Request.Query);

                // response_mode=query (explicit or not) and a response_type containing id_token
                // or token are not considered as a safe combination and MUST be rejected.
                // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
                if (!string.IsNullOrEmpty(openIdConnectMessage.IdToken) ||
                    !string.IsNullOrEmpty(openIdConnectMessage.AccessToken))
                {
                    _logger.WriteWarning("An OpenID Connect response cannot contain an " +
                                         "identity token or an access token when using response_mode=query");
                    return(null);
                }
            }
            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                     !string.IsNullOrWhiteSpace(Request.ContentType)
                     // May have media/type; charset=utf-8, allow partial match.
                     && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                     Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    // Buffer in case this body was not meant for us.
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }

                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message.
                openIdConnectMessage = new OpenIdConnectMessage(form);
            }

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

            ExceptionDispatchInfo authFailedEx = null;

            try
            {
                // Run MessageReceived notification
                {
                    var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                    {
                        ProtocolMessage = openIdConnectMessage
                    };
                    await Options.Notifications.MessageReceived(messageReceivedNotification);

                    if (messageReceivedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (messageReceivedNotification.Skipped)
                    {
                        return(null);
                    }
                }

                // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
                // should process.
                AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
                if (properties == null)
                {
                    _logger.WriteWarning("The state field is missing or invalid.");
                    return(null);
                }

                // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
                {
                    throw new OpenIdConnectProtocolException(
                              string.Format(CultureInfo.InvariantCulture,
                                            Resources.Exception_OpenIdConnectMessageError,
                                            openIdConnectMessage.Error, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
                }

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled);
                }

                JwtSecurityToken     jwt       = null;
                ClaimsPrincipal      principal = null;
                AuthenticationTicket ticket    = null;
                string nonce = null;
                OpenIdConnectMessage tokenEndpointResponse = null;
                JwtSecurityToken     tokenEndpointJwt      = null;

                // Copy and augment to avoid cross request race conditions for updated configurations.
                TokenValidationParameters tvp = Options.TokenValidationParameters.Clone();

                // Hybrid or Implicit flow
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
                {
                    // Run SecurityTokenReceived notification
                    {
                        var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                        {
                            ProtocolMessage = openIdConnectMessage,
                        };
                        await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                        if (securityTokenReceivedNotification.HandledResponse)
                        {
                            return(GetHandledResponseTicket());
                        }
                        if (securityTokenReceivedNotification.Skipped)
                        {
                            return(null);
                        }
                    }

                    principal = ValidateToken(openIdConnectMessage.IdToken, properties, tvp, out jwt);
                    ticket    = new AuthenticationTicket(principal.Identity as ClaimsIdentity, properties);

                    if (Options.ProtocolValidator.RequireNonce)
                    {
                        if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce))
                        {
                            openIdConnectMessage.Nonce = jwt.Payload.Nonce;
                        }

                        // deletes the nonce cookie
                        nonce = RetrieveNonce(openIdConnectMessage);
                    }

                    // remember 'session_state' and 'check_session_iframe'
                    if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
                    {
                        properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
                    }

                    if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
                    {
                        properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
                    }

                    // Run SecurityTokenValidated notification
                    {
                        var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                        {
                            AuthenticationTicket = ticket,
                            ProtocolMessage      = openIdConnectMessage,
                        };
                        await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                        if (securityTokenValidatedNotification.HandledResponse)
                        {
                            return(GetHandledResponseTicket());
                        }
                        if (securityTokenValidatedNotification.Skipped)
                        {
                            return(null);
                        }

                        // Flow possible changes
                        ticket = securityTokenValidatedNotification.AuthenticationTicket;
                    }
                }

                Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext()
                {
                    ClientId         = Options.ClientId,
                    ProtocolMessage  = openIdConnectMessage,
                    ValidatedIdToken = jwt,
                    Nonce            = nonce
                });

                // Authorization Code or Hybrid flow
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Code))
                {
                    var tokenEndpointRequest = new OpenIdConnectMessage()
                    {
                        ClientId     = Options.ClientId,
                        ClientSecret = Options.ClientSecret,
                        Code         = openIdConnectMessage.Code,
                        GrantType    = OpenIdConnectGrantTypes.AuthorizationCode,
                        RedirectUri  = properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                                       properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty
                    };

                    // Run AuthorizationCodeReceived notification
                    {
                        var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                        {
                            TokenEndpointRequest = tokenEndpointRequest,
                            Code             = openIdConnectMessage.Code,
                            JwtSecurityToken = jwt,
                            ProtocolMessage  = openIdConnectMessage,
                            RedirectUri      = tokenEndpointRequest.RedirectUri,
                        };
                        await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);

                        if (authorizationCodeReceivedNotification.HandledResponse)
                        {
                            return(GetHandledResponseTicket());
                        }
                        if (authorizationCodeReceivedNotification.Skipped)
                        {
                            return(null);
                        }
                        tokenEndpointRequest = authorizationCodeReceivedNotification.TokenEndpointRequest;
                        openIdConnectMessage = authorizationCodeReceivedNotification.ProtocolMessage;
                    }

                    // Authorization Code flow - no Id Token was received
                    if (ticket == null)
                    {
                        // Redeem token using the received authorization code
                        tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest);

                        // Run SecurityTokenReceived notification
                        {
                            var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                            {
                                ProtocolMessage = tokenEndpointResponse,
                            };
                            await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                            if (securityTokenReceivedNotification.HandledResponse)
                            {
                                return(GetHandledResponseTicket());
                            }
                            if (securityTokenReceivedNotification.Skipped)
                            {
                                return(null);
                            }
                        }

                        // Validate token

                        // no need to validate signature when token is received using "code flow" as per spec
                        // [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation].
                        tvp.RequireSignedTokens = false;

                        // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response.
                        // And we'll want to validate the new JWT in ValidateTokenResponse.
                        principal = ValidateToken(tokenEndpointResponse.IdToken, properties, tvp, out tokenEndpointJwt);

                        ticket = new AuthenticationTicket(principal.Identity as ClaimsIdentity, properties);

                        nonce = tokenEndpointJwt.Payload.Nonce;
                        if (String.IsNullOrWhiteSpace(nonce))
                        {
                            nonce = ReadNonceCookie(nonce);
                        }

                        // Run SecurityTokenValidated notification
                        {
                            var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                            {
                                AuthenticationTicket = ticket,
                                ProtocolMessage      = tokenEndpointResponse,
                            };
                            await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                            if (securityTokenValidatedNotification.HandledResponse)
                            {
                                return(GetHandledResponseTicket());
                            }
                            if (securityTokenValidatedNotification.Skipped)
                            {
                                return(null);
                            }
                            // Flow possible changes
                            ticket = securityTokenValidatedNotification.AuthenticationTicket;
                        }
                    }
                }

                if (Options.GetClaimsFromUserInfoEndpoint)
                {
                    await GetUserInformationAsync(
                        tokenEndpointResponse ?? openIdConnectMessage,
                        tokenEndpointJwt ?? jwt,
                        principal,
                        properties);
                }

                return(ticket);
            }
            catch (Exception exception)
            {
                // We can't await inside a catch block, capture and handle outside.
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
 private static Task MessageReceived(MessageReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions> notification)
 {
     notification.Token = "CustomToken";
     return(Task.FromResult <object>(null));
 }
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path. Need to make this runtime configurable.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            OpenIdConnectMessage openIdConnectMessage = null;

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                !string.IsNullOrWhiteSpace(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    // Buffer in case this body was not meant for us.
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }

                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message.
                openIdConnectMessage = new OpenIdConnectMessage(form);
            }

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

            ExceptionDispatchInfo authFailedEx = null;

            try
            {
                var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
                // should process.
                AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
                if (properties == null)
                {
                    _logger.WriteWarning("The state field is missing or invalid.");
                    return(null);
                }

                // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
                {
                    throw new OpenIdConnectProtocolException(
                              string.Format(CultureInfo.InvariantCulture,
                                            openIdConnectMessage.Error,
                                            Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
                }

                // code is only accepted with id_token, in this version, hence check for code is inside this if
                // OpenIdConnect protocol allows a Code to be received without the id_token
                if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
                {
                    _logger.WriteWarning("The id_token is missing.");
                    return(null);
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled);
                }

                // Copy and augment to avoid cross request race conditions for updated configurations.
                TokenValidationParameters tvp     = Options.TokenValidationParameters.Clone();
                IEnumerable <string>      issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers        = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningTokens = (tvp.IssuerSigningTokens == null ? _configuration.SigningTokens : tvp.IssuerSigningTokens.Concat(_configuration.SigningTokens));

                SecurityToken   validatedToken;
                ClaimsPrincipal principal      = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken);
                ClaimsIdentity  claimsIdentity = principal.Identity as ClaimsIdentity;

                // claims principal could have changed claim values, use bits received on wire for validation.
                JwtSecurityToken     jwt    = validatedToken as JwtSecurityToken;
                AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties);

                string nonce = null;
                if (Options.ProtocolValidator.RequireNonce)
                {
                    if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce))
                    {
                        openIdConnectMessage.Nonce = jwt.Payload.Nonce;
                    }

                    // deletes the nonce cookie
                    nonce = RetrieveNonce(openIdConnectMessage);
                }

                // remember 'session_state' and 'check_session_iframe'
                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
                }

                if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
                }

                if (Options.UseTokenLifetime)
                {
                    // Override any session persistence to match the token lifetime.
                    DateTime issued = jwt.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        ticket.Properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    DateTime expires = jwt.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    ticket.Properties.AllowRefresh = false;
                }

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null);
                }
                // Flow possible changes
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                var protocolValidationContext = new OpenIdConnectProtocolValidationContext
                {
                    AuthorizationCode = openIdConnectMessage.Code,
                    Nonce             = nonce,
                };

                Options.ProtocolValidator.Validate(jwt, protocolValidationContext);

                if (openIdConnectMessage.Code != null)
                {
                    var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        Code             = openIdConnectMessage.Code,
                        JwtSecurityToken = jwt,
                        ProtocolMessage  = openIdConnectMessage,
                        RedirectUri      = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                                           ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
                    };
                    await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);

                    if (authorizationCodeReceivedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (authorizationCodeReceivedNotification.Skipped)
                    {
                        return(null);
                    }
                    // Flow possible changes
                    ticket = authorizationCodeReceivedNotification.AuthenticationTicket;
                }

                return(ticket);
            }
            catch (Exception exception)
            {
                // We can't await inside a catch block, capture and handle outside.
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: '" + authFailedEx.ToString());

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            // Allow login to be constrained to a specific path. Need to make this runtime configurable.
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return(null);
            }

            OpenIdConnectMessage authorizationResponse = null;

            if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase) && Request.Query.Any())
            {
                authorizationResponse = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value)));

                // response_mode=query (explicit or not) and a response_type containing id_token
                // or token are not considered as a safe combination and MUST be rejected.
                // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
                if (!string.IsNullOrEmpty(authorizationResponse.IdToken) || !string.IsNullOrEmpty(authorizationResponse.AccessToken))
                {
                    var invalidResponseEx = new OpenIdConnectProtocolException("An OpenID Connect response cannot contain an identity token or an access token when using response_mode=query");

                    _logger.WriteError("Exception occurred while processing message: ", invalidResponseEx);

                    var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                    {
                        ProtocolMessage = authorizationResponse,
                        Exception       = invalidResponseEx
                    };
                    await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                    if (authenticationFailedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (authenticationFailedNotification.Skipped)
                    {
                        return(null);
                    }

                    throw invalidResponseEx;
                }
            }
            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
                     !string.IsNullOrWhiteSpace(Request.ContentType)
                     // May have media/type; charset=utf-8, allow partial match.
                     && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                     Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    // Buffer in case this body was not meant for us.
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }

                IFormCollection form = await Request.ReadFormAsync();

                Request.Body.Seek(0, SeekOrigin.Begin);

                // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message.
                authorizationResponse = new OpenIdConnectMessage(form);
            }

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

            ExceptionDispatchInfo authFailedEx = null;

            try
            {
                var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = authorizationResponse
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
                // should process.
                AuthenticationProperties properties = GetPropertiesFromState(authorizationResponse.State);
                if (properties == null)
                {
                    _logger.WriteWarning("The state field is missing or invalid.");
                    return(null);
                }

                // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
                if (!string.IsNullOrWhiteSpace(authorizationResponse.Error))
                {
                    throw CreateOpenIdConnectProtocolException(authorizationResponse);
                }

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled);
                }

                PopulateSessionProperties(authorizationResponse, properties);

                ClaimsPrincipal      user   = null;
                AuthenticationTicket ticket = null;
                JwtSecurityToken     jwt    = null;
                string nonce = null;
                // Copy and augment to avoid cross request race conditions for updated configurations.
                var validationParameters = Options.TokenValidationParameters.Clone();

                // Hybrid or Implicit flow
                if (!string.IsNullOrEmpty(authorizationResponse.IdToken))
                {
                    var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                    {
                        ProtocolMessage = authorizationResponse,
                    };
                    await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                    if (securityTokenReceivedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (securityTokenReceivedNotification.Skipped)
                    {
                        return(null);
                    }

                    user = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt);

                    if (Options.ProtocolValidator.RequireNonce)
                    {
                        if (string.IsNullOrWhiteSpace(authorizationResponse.Nonce))
                        {
                            authorizationResponse.Nonce = jwt.Payload.Nonce;
                        }

                        // deletes the nonce cookie
                        nonce = RetrieveNonce(authorizationResponse);
                    }

                    ClaimsIdentity claimsIdentity = user.Identity as ClaimsIdentity;
                    ticket = new AuthenticationTicket(claimsIdentity, properties);

                    var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        ProtocolMessage      = authorizationResponse,
                    };
                    await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                    if (securityTokenValidatedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (securityTokenValidatedNotification.Skipped)
                    {
                        return(null);
                    }
                    // Flow possible changes
                    ticket = securityTokenValidatedNotification.AuthenticationTicket;
                }

                Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext()
                {
                    ClientId         = Options.ClientId,
                    ProtocolMessage  = authorizationResponse,
                    ValidatedIdToken = jwt,
                    Nonce            = nonce
                });

                OpenIdConnectMessage tokenEndpointResponse = null;

                // Authorization Code or Hybrid flow
                if (!string.IsNullOrEmpty(authorizationResponse.Code))
                {
                    var tokenEndpointRequest = new OpenIdConnectMessage()
                    {
                        ClientId     = Options.ClientId,
                        ClientSecret = Options.ClientSecret,
                        Code         = authorizationResponse.Code,
                        GrantType    = OpenIdConnectGrantTypes.AuthorizationCode,
                        RedirectUri  = properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                                       properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
                    };

                    var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        Code                 = authorizationResponse.Code,
                        JwtSecurityToken     = jwt,
                        ProtocolMessage      = authorizationResponse,
                        RedirectUri          = tokenEndpointRequest.RedirectUri,
                        TokenEndpointRequest = tokenEndpointRequest
                    };

                    // PKCE https://tools.ietf.org/html/rfc7636#section-4.5
                    string codeVerifier;
                    if (properties.Dictionary.TryGetValue(OAuthConstants.CodeVerifierKey, out codeVerifier))
                    {
                        tokenEndpointRequest.Parameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
                        properties.Dictionary.Remove(OAuthConstants.CodeVerifierKey);
                    }

                    await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);

                    if (authorizationCodeReceivedNotification.HandledResponse)
                    {
                        return(GetHandledResponseTicket());
                    }
                    if (authorizationCodeReceivedNotification.Skipped)
                    {
                        return(null);
                    }
                    // Flow possible changes
                    authorizationResponse = authorizationCodeReceivedNotification.ProtocolMessage;
                    ticket = authorizationCodeReceivedNotification.AuthenticationTicket;
                    tokenEndpointRequest  = authorizationCodeReceivedNotification.TokenEndpointRequest;
                    tokenEndpointResponse = authorizationCodeReceivedNotification.TokenEndpointResponse;
                    jwt = authorizationCodeReceivedNotification.JwtSecurityToken;

                    if (!authorizationCodeReceivedNotification.HandledCodeRedemption && Options.RedeemCode)
                    {
                        tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest);
                    }

                    if (tokenEndpointResponse != null)
                    {
                        var tokenResponseReceivedNotification = new TokenResponseReceivedNotification(Context, Options)
                        {
                            ProtocolMessage       = authorizationResponse,
                            TokenEndpointResponse = tokenEndpointResponse
                        };
                        await Options.Notifications.TokenResponseReceived(tokenResponseReceivedNotification);

                        if (tokenResponseReceivedNotification.HandledResponse)
                        {
                            return(GetHandledResponseTicket());
                        }
                        if (tokenResponseReceivedNotification.Skipped)
                        {
                            return(null);
                        }

                        // no need to validate signature when token is received using "code flow" as per spec
                        // [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation].
                        validationParameters.RequireSignedTokens = false;

                        // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response.
                        // And we'll want to validate the new JWT in ValidateTokenResponse.
                        JwtSecurityToken tokenEndpointJwt = null;
                        var tokenEndpointUser             = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt);

                        // Avoid running the event, etc, if it was already done as part of the authorization response validation.
                        if (user == null)
                        {
                            if (Options.ProtocolValidator.RequireNonce)
                            {
                                if (string.IsNullOrWhiteSpace(tokenEndpointResponse.Nonce))
                                {
                                    tokenEndpointResponse.Nonce = tokenEndpointJwt.Payload.Nonce;
                                }

                                // deletes the nonce cookie
                                if (nonce == null)
                                {
                                    nonce = RetrieveNonce(tokenEndpointResponse);
                                }
                            }

                            ClaimsIdentity claimsIdentity = tokenEndpointUser.Identity as ClaimsIdentity;
                            ticket = new AuthenticationTicket(claimsIdentity, properties);

                            var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                            {
                                AuthenticationTicket = ticket,
                                ProtocolMessage      = tokenEndpointResponse,
                            };
                            await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                            if (securityTokenValidatedNotification.HandledResponse)
                            {
                                return(GetHandledResponseTicket());
                            }
                            if (securityTokenValidatedNotification.Skipped)
                            {
                                return(null);
                            }
                            // Flow possible changes
                            ticket = securityTokenValidatedNotification.AuthenticationTicket;
                        }
                        else
                        {
                            if (!string.Equals(jwt.Subject, tokenEndpointJwt.Subject, StringComparison.Ordinal))
                            {
                                throw new SecurityTokenException("The sub claim does not match in the id_token's from the authorization and token endpoints.");
                            }
                        }

                        jwt = tokenEndpointJwt;
                    }

                    // Validate the token response if it wasn't provided manually
                    if (!authorizationCodeReceivedNotification.HandledCodeRedemption && Options.RedeemCode)
                    {
                        Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext()
                        {
                            ClientId         = Options.ClientId,
                            ProtocolMessage  = tokenEndpointResponse,
                            ValidatedIdToken = jwt,
                            Nonce            = nonce
                        });
                    }
                }

                if (Options.SaveTokens && ticket != null)
                {
                    SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse);
                }

                return(ticket);
            }
            catch (Exception exception)
            {
                // We can't await inside a catch block, capture and handle outside.
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = authorizationResponse,
                    Exception       = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(GetHandledResponseTicket());
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                authFailedEx.Throw();
            }

            return(null);
        }
        /// <summary>
        /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options.
        /// </summary>
        /// <returns></returns>
        protected override async Task <AuthenticationTicket> HandleAuthenticateAsync()
        {
            string token = null;

            try
            {
                // Give application opportunity to find from a different location, adjust, or reject token
                var messageReceivedNotification =
                    new MessageReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = Context,
                };

                // notification can set the token
                await Options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(messageReceivedNotification.AuthenticationTicket);
                }

                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }

                // If application retrieved token from somewhere else, use that.
                token = messageReceivedNotification.Token;

                if (string.IsNullOrEmpty(token))
                {
                    var authorization = Request.Headers.Get("Authorization");

                    // If no authorization header found, nothing to process further
                    if (string.IsNullOrEmpty(authorization))
                    {
                        return(null);
                    }

                    if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                    {
                        token = authorization.Substring("Bearer ".Length).Trim();
                    }

                    // If no token found, no further work possible
                    if (string.IsNullOrEmpty(token))
                    {
                        return(null);
                    }
                }

                // notify user token was received
                var securityTokenReceivedNotification =
                    new SecurityTokenReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = Context,
                    SecurityToken   = token,
                };

                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(securityTokenReceivedNotification.AuthenticationTicket);
                }

                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                if (_configuration == null && Options.ConfigurationManager != null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
                }

                var validationParameters = Options.TokenValidationParameters.Clone();
                if (_configuration != null)
                {
                    if (validationParameters.ValidIssuer == null && !string.IsNullOrWhiteSpace(_configuration.Issuer))
                    {
                        validationParameters.ValidIssuer = _configuration.Issuer;
                    }
                    else
                    {
                        var issuers = new[] { _configuration.Issuer };
                        validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? issuers : validationParameters.ValidIssuers.Concat(issuers));
                    }

                    validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
                }

                SecurityToken validatedToken;
                foreach (var validator in Options.SecurityTokenValidators)
                {
                    if (validator.CanReadToken(token))
                    {
                        var principal = validator.ValidateToken(token, validationParameters, out validatedToken);
                        var ticket    = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme);
                        var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options)
                        {
                            ProtocolMessage      = Context,
                            AuthenticationTicket = ticket
                        };

                        await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                        if (securityTokenValidatedNotification.HandledResponse)
                        {
                            return(securityTokenValidatedNotification.AuthenticationTicket);
                        }

                        if (securityTokenValidatedNotification.Skipped)
                        {
                            return(null);
                        }

                        return(ticket);
                    }
                }

                throw new InvalidOperationException("No SecurityTokenValidator available for token: " + token ?? "null");
            }
            catch (Exception ex)
            {
                Logger.LogError("Exception occurred while processing message", ex);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && ex.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedNotification =
                    new AuthenticationFailedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = Context,
                    Exception       = ex
                };

                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(authenticationFailedNotification.AuthenticationTicket);
                }

                if (authenticationFailedNotification.Skipped)
                {
                    return(null);
                }

                throw;
            }
        }
 internal static async Task MessageReceived(MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
 {
     Helpers.ThrowIfConditionFailed(() => context.ProtocolMessage != null, "ProtocolMessage is null.");
     notificationsFired.Add(nameof(MessageReceived));
     await Task.FromResult(0);
 }
Example #33
0
 private Task OnMessageReceived(MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> aArg)
 {
     return(Task.FromResult(0));
 }
Example #34
0
        /// <summary>
        /// Invokes the login procedure (2nd leg of SP-Initiated login). Analagous to Saml20SignonHandler from ASP.Net DLL
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task <AuthenticationTicket> Invoke(IOwinContext context)
        {
            Logger.Debug(TraceMessages.SignOnHandlerCalled);
            ExceptionDispatchInfo authFailedEx = null;

            try {
                var messageReceivedNotification = new MessageReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = new SamlMessage(context, configuration, null)
                };
                await options.Notifications.MessageReceived(messageReceivedNotification);

                if (messageReceivedNotification.HandledResponse)
                {
                    return(null); // GetHandledResponseTicket()
                }
                if (messageReceivedNotification.Skipped)
                {
                    return(null);
                }
                var requestParams = await HandleResponse(context);

                var assertion = context.Get <Saml20Assertion>("Saml2:assertion");
                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = new SamlMessage(context, configuration, assertion)
                };
                await options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);

                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return(null); // GetHandledResponseTicket();
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return(null);
                }

                var ticket = await GetAuthenticationTicket(context, requestParams);

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage      = new SamlMessage(context, configuration, assertion)
                };

                await options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);

                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return(null); // GetHandledResponseTicket();
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return(null); // null;
                }
                // Flow possible changes
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(ticket.Identity, ticket.Properties);
                return(ticket);
            }
            catch (Exception ex) {
                authFailedEx = ExceptionDispatchInfo.Capture(ex);
            }
            if (authFailedEx != null)
            {
                Logger.Error("Exception occurred while processing message: " + authFailedEx.SourceException);
                var message = new SamlMessage(context, configuration, context.Get <Saml20Assertion>("Saml2:assertion"));
                var authenticationFailedNotification = new AuthenticationFailedNotification <SamlMessage, SamlAuthenticationOptions>(context, options)
                {
                    ProtocolMessage = message,
                    Exception       = authFailedEx.SourceException
                };
                await options.Notifications.AuthenticationFailed(authenticationFailedNotification);

                if (authenticationFailedNotification.HandledResponse)
                {
                    return(null);//GetHandledResponseTicket();
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return(null); //null
                }

                authFailedEx.Throw();
            }
            return(null);
        }
        protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
        {
            if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
            {
                return null;
            }

            OpenIdConnectMessage openIdConnectMessage = null;

            if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
              && !string.IsNullOrWhiteSpace(Request.ContentType)
              && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
              && Request.Body.CanRead)
            {
                if (!Request.Body.CanSeek)
                {
                    _logger.WriteVerbose("Buffering request body");
                    MemoryStream memoryStream = new MemoryStream();
                    await Request.Body.CopyToAsync(memoryStream);
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    Request.Body = memoryStream;
                }

                IFormCollection form = await Request.ReadFormAsync();
                Request.Body.Seek(0, SeekOrigin.Begin);

                openIdConnectMessage = new OpenIdConnectMessage(form);
            }

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

            ExceptionDispatchInfo authFailedEx = null;
            string policy = string.Empty;
            try
            {
                var messageReceivedNotification = new MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage
                };
                await Options.Notifications.MessageReceived(messageReceivedNotification);
                if (messageReceivedNotification.HandledResponse)
                {
                    return GetHandledResponseTicket();
                }
                if (messageReceivedNotification.Skipped)
                {
                    return null;
                }

                AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
                if (properties == null)
                {
                    _logger.WriteWarning("The state field is missing or invalid.");
                    return null;
                }

                string nonce = null;
                if (Options.ProtocolValidator.RequireNonce)
                {
                    nonce = RetrieveNonce(openIdConnectMessage);
                }

                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
                {
                    throw new OpenIdConnectProtocolException(
                        string.Format(CultureInfo.InvariantCulture,
                                      openIdConnectMessage.Error,
                                      "", openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
                }

                if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
                {
                    _logger.WriteWarning("The id_token is missing.");
                    return null;
                }

                var securityTokenReceivedNotification = new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
                if (securityTokenReceivedNotification.HandledResponse)
                {
                    return GetHandledResponseTicket();
                }
                if (securityTokenReceivedNotification.Skipped)
                {
                    return null;
                }

                // Enable Per-Policy Metadata Retreival
                if (properties.Dictionary.TryGetValue(PolicyParameter, out policy))
                {
                    B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager;
                    _configuration = await mgr.GetConfigurationAsync(Context.Request.CallCancelled, policy);
                }
                else
                {
                    _logger.WriteWarning("No policy identifier was found in the Authentication Properties of the request.");
                    return null;
                }

                TokenValidationParameters tvp = Options.TokenValidationParameters.Clone();
                IEnumerable<string> issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningTokens = (tvp.IssuerSigningTokens == null ? _configuration.SigningTokens : tvp.IssuerSigningTokens.Concat(_configuration.SigningTokens));

                SecurityToken validatedToken;
                ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken);
                ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity;

                JwtSecurityToken jwt = validatedToken as JwtSecurityToken;
                AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties);

                if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
                }

                if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
                {
                    ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
                }

                if (Options.UseTokenLifetime)
                {
                    DateTime issued = jwt.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        ticket.Properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    DateTime expires = jwt.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        ticket.Properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    ticket.Properties.AllowRefresh = false;
                }

                var securityTokenValidatedNotification = new SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    AuthenticationTicket = ticket,
                    ProtocolMessage = openIdConnectMessage,
                };
                await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
                if (securityTokenValidatedNotification.HandledResponse)
                {
                    return GetHandledResponseTicket();
                }
                if (securityTokenValidatedNotification.Skipped)
                {
                    return null;
                }
                ticket = securityTokenValidatedNotification.AuthenticationTicket;

                var protocolValidationContext = new OpenIdConnectProtocolValidationContext
                {
                    AuthorizationCode = openIdConnectMessage.Code,
                    Nonce = nonce,
                };

                Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
                if (openIdConnectMessage.Code != null)
                {
                    var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
                    {
                        AuthenticationTicket = ticket,
                        Code = openIdConnectMessage.Code,
                        JwtSecurityToken = jwt,
                        ProtocolMessage = openIdConnectMessage,
                        RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
                            ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
                    };
                    await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
                    if (authorizationCodeReceivedNotification.HandledResponse)
                    {
                        return GetHandledResponseTicket();
                    }
                    if (authorizationCodeReceivedNotification.Skipped)
                    {
                        return null;
                    }
                    ticket = authorizationCodeReceivedNotification.AuthenticationTicket;
                }

                return ticket;
            }
            catch (Exception exception)
            {
                authFailedEx = ExceptionDispatchInfo.Capture(exception);
            }

            if (authFailedEx != null)
            {
                _logger.WriteError("Exception occurred while processing message: '" + authFailedEx.ToString());

                if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager;
                    mgr.RequestRefresh(policy);
                }

                var authenticationFailedNotification = new AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
                {
                    ProtocolMessage = openIdConnectMessage,
                    Exception = authFailedEx.SourceException
                };
                await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
                if (authenticationFailedNotification.HandledResponse)
                {
                    return GetHandledResponseTicket();
                }
                if (authenticationFailedNotification.Skipped)
                {
                    return null;
                }

                authFailedEx.Throw();
            }

            return null;
        }