/// <summary>
        /// Sets SSO token. Default implementation sets browser cookie
        /// </summary>
        protected virtual void SetSsoSessionId(LoginFlow loginFlow, string ssoSessionName)
        {
            var cookie = new Cookie(ssoSessionName, loginFlow.SsoSessionId);

            cookie.HttpOnly = true;
            cookie.Secure   = true ^ WebOptions.Of("cookie-not-secure").ValueAsBool(false);
            cookie.Expires  = App.TimeSource.UTCNow.AddMinutes(WebOptions.Of("cookie-expire-in-minutes").ValueAsDouble(2 * 24 * 60));
            WorkContext.Response.AppendCookie(cookie);
        }
        protected async virtual Task SetSsoSubjectSessionAsync(LoginFlow loginFlow, DateTime lastLoginUtc, User ssoSubjectUser)
        {
            loginFlow.SsoWasJustSet          = true;
            loginFlow.SsoSubjectLastLoginUtc = lastLoginUtc;
            loginFlow.SsoSubjectUser         = ssoSubjectUser;
            var session = OAuth.TokenRing.GenerateNew <SsoSessionToken>();

            session.SysAuthToken   = loginFlow.SsoSubjectUser.AuthToken.ToString();
            loginFlow.SsoSessionId = await OAuth.TokenRing.PutAsync(session).ConfigureAwait(false);
        }
        /// <summary>
        /// Override to provide a authorize result which is by default either a stock login form or
        /// JSON object
        /// </summary>
        protected virtual object MakeAuthorizeResult(LoginFlow loginFlow, string roundtrip, string error)
        {
            if (WorkContext.RequestedJson)
            {
                return new { OK = error.IsNullOrEmpty(), roundtrip, error }
            }
            ;

            //stock log-in page
            return(new Wave.Templatization.StockContent.OAuthLogin(loginFlow.ClientUser, roundtrip, error));
        }
 /// <summary>
 /// Advances login flow state to the next level.
 /// You may override this and accompanying "RespondWithAuthorizeResult/MakeAuthorizeResult"/>
 /// method to build a complex login flows which return different views, such as 2FA etc.
 /// </summary>
 protected virtual Task AdvanceLoginFlowStateAsync(LoginFlow loginFlow)
 {
     if (loginFlow.IsValidSsoUser)
     {
         loginFlow.FiniteStateSuccess = true;
     }
     if (loginFlow.IsValidSubjectUser)
     {
         loginFlow.FiniteStateSuccess = true;
     }
     return(Task.CompletedTask);
 }
        protected virtual object RespondWithAuthorizeResult(long sdUtc, LoginFlow loginFlow, string error)
        {
            //Pack all requested content(session) into cryptographically encoded message aka "roundtrip"
            var flow = new {
                sd  = sdUtc,
                iss = App.TimeSource.UTCNow.ToSecondsSinceUnixEpochStart(),
                tp  = loginFlow.ClientResponseType,
                scp = loginFlow.ClientScope,
                id  = loginFlow.ClientId,
                uri = loginFlow.ClientRedirectUri,
                st  = loginFlow.ClientState
            };
            var roundtrip = App.SecurityManager.PublicProtectAsString(flow);

            if (error != null)
            {
                WorkContext.Response.StatusCode        = WebConsts.STATUS_403;
                WorkContext.Response.StatusDescription = error;
            }

            return(MakeAuthorizeResult(loginFlow, roundtrip, error));
        }
        /// <summary>
        /// Tries to get SSO subject user by examining the supplied idSsoSession.
        /// Set sso user to NULL if the SSO session id is invalid/or user revoked etc..
        /// </summary>
        protected async virtual Task TryGetSsoSubjectAsync(LoginFlow loginFlow)
        {
            var session = await OAuth.TokenRing.GetAsync <SsoSessionToken>(loginFlow.SsoSessionId).ConfigureAwait(false);

            if (session == null)
            {
                return;
            }

            if (!SysAuthToken.TryParse(session.SysAuthToken, out var sysToken))
            {
                return;
            }

            var ssoSubject = await App.SecurityManager.AuthenticateAsync(sysToken).ConfigureAwait(false);

            if (!ssoSubject.IsAuthenticated)
            {
                return;                        //sys auth token may have expired
            }
            loginFlow.SsoSubjectUser = ssoSubject;
        }
        /// <summary>
        /// Override to extract SSO session cookie from the request. The value (if any) is set on `loginFlow.SsoSessionId`
        /// Default implementation uses Request Cookie named OAuth.SsoSessionName if it is set.
        /// You must set loginFlow.SsoSessionId to null if there is no such session id provided or OAuth.ssoSessionName is turned off (null or whitespace)
        /// </summary>
        protected virtual void TryExtractSsoSessionId(LoginFlow loginFlow)
        {
            var ssoCookieName = OAuth.SsoSessionName;//copy

            if (ssoCookieName.IsNullOrWhiteSpace())
            {
                return;
            }
            var cookie = WorkContext.Request.Cookies[ssoCookieName];

            if (cookie == null)
            {
                return;
            }
            var result = cookie.Value;

            if (result.IsNullOrWhiteSpace())
            {
                return;
            }

            loginFlow.SsoSessionId = result;
        }
 /// <summary>
 /// Override to add extra information to <see cref="AuthenticationRequestContext"/>.
 /// Default implementation does nothing
 /// </summary>
 protected virtual void ConfigureAuthenticationRequestContext(LoginFlow loginFlow, AuthenticationRequestContext context)
 {
 }
 /// <summary>
 /// Override to return a 403 (unauthorized result) of user logout
 /// </summary>
 protected virtual object ReturnSsoLogout403(LoginFlow flow)
 {
     return(new Http403Forbidden());
 }