/// <summary> /// Emits the current session tokens with an updated expiration time /// </summary> /// <returns>An awaitable task.</returns> /// <param name="context">The http context.</param> /// <param name="session">The current session record.</param> protected virtual async Task RefreshSessionTokensAsync(IHttpContext context, SessionRecord session) { if (session == null) { throw new ArgumentNullException(nameof(session)); } // Renew if the token is starting to get old if ((session.Expires - DateTime.Now).TotalSeconds < ShortTermRefreshThreshold) { // If the connection is using SSL, require SSL for the cookie var usingssl = context.Request.SslProtocol != System.Security.Authentication.SslProtocols.None; session.Expires = DateTime.Now.AddSeconds(ShortTermExpirationSeconds); await ShortTermStorage.UpdateSessionExpirationAsync(session); if (!string.IsNullOrWhiteSpace(session.XSRFToken)) { context.Response.AddCookie(XSRFCookieName, session.XSRFToken, expires: session.Expires, httponly: false, path: CookiePath, secure: usingssl); } if (!string.IsNullOrWhiteSpace(session.Cookie)) { context.Response.AddCookie(AuthSessionCookieName, session.Cookie, expires: session.Expires, httponly: true, path: CookiePath, secure: usingssl); } } }
/// <summary> /// Drops a session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to drop.</param> public virtual Task DropSessionAsync(SessionRecord record) { return(ExecuteCommandAsync( m_dropSessionCommand, record.UserID, record.Cookie, record.XSRFToken )); }
/// <summary> /// Handles the request /// </summary> /// <returns>The awaitable task.</returns> /// <param name="context">The requests context.</param> public async Task <bool> HandleAsync(IHttpContext context) { var xsrf = context.Request.Headers[XSRFHeaderName] ?? context.Request.Cookies[XSRFCookieName]; var cookie = context.Request.Cookies[AuthSessionCookieName]; SessionRecord session = null; if (!string.IsNullOrWhiteSpace(xsrf)) { session = await ShortTermStorage.GetSessionFromXSRFAsync(xsrf); if (Utility.IsNullOrExpired(session)) { session = null; } else { await RefreshSessionTokensAsync(context, session); } } if (session == null && !string.IsNullOrWhiteSpace(cookie)) { session = await ShortTermStorage.GetSessionFromCookieAsync(cookie); if (Utility.IsNullOrExpired(session)) { session = null; } else { await RefreshSessionTokensAsync(context, session); } } // Check that we have not already set the XSRF cookie if (context.Response.Cookies.FirstOrDefault(x => x.Name == XSRFCookieName) == null) { if (session == null) { session = new SessionRecord() { XSRFToken = PRNG.GetRandomString(32), Expires = DateTime.Now.AddSeconds(ShortTermExpirationSeconds) }; await ShortTermStorage.AddSessionAsync(session); } // If the connection is using SSL, require SSL for the cookie var usingssl = context.Request.SslProtocol != System.Security.Authentication.SslProtocols.None; context.Response.AddCookie(XSRFCookieName, session.XSRFToken, expires: session.Expires, httponly: false, secure: usingssl); } return(false); }
/// <summary> /// Updates the expiration time on the given session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to update.</param> public virtual Task UpdateSessionExpirationAsync(SessionRecord record) { return(ExecuteCommandAsync( m_updateSessionCommand, record.Expires, record.UserID, record.Cookie, record.XSRFToken )); }
/// <summary> /// Adds a new session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to add.</param> public virtual Task AddSessionAsync(SessionRecord record) { return(ExecuteCommandAsync( m_addSessionCommand, record.UserID, record.Cookie, record.XSRFToken, record.Expires )); }
/// <summary> /// Drops a session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to drop.</param> public virtual Task DropSessionAsync(SessionRecord record) { return(m_lock.LockedAsync(() => m_connection.Delete <SessionRecord>(x => x.UserID == record.UserID && x.Cookie == record.Cookie && x.XSRFToken == record.XSRFToken ) )); }
/// <summary> /// Updates the expiration time on the given session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to update.</param> public virtual Task UpdateSessionExpirationAsync(SessionRecord record) { return(m_lock.LockedAsync(() => m_connection.Update <SessionRecord>( new { record.Expires }, x => x.UserID == record.UserID && x.Cookie == record.Cookie && x.XSRFToken == record.XSRFToken ) )); }
/// <summary> /// Drops a session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to drop.</param> public async Task DropSessionAsync(SessionRecord record) { using (await m_lock.LockAsync()) { if (!string.IsNullOrWhiteSpace(record.Cookie)) { m_cookie_storage.Remove(record.Cookie); } if (!string.IsNullOrWhiteSpace(record.XSRFToken)) { m_xsrf_storage.Remove(record.XSRFToken); } } }
/// <summary> /// Updates the expiration time on the given session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to update.</param> public async Task UpdateSessionExpirationAsync(SessionRecord record) { var txt = PrimitiveSerializer.Serialize(record); using (await m_lock.LockAsync()) { if (!string.IsNullOrWhiteSpace(record.XSRFToken)) { m_xsrf_storage[record.XSRFToken] = txt; } if (!string.IsNullOrWhiteSpace(record.Cookie)) { m_cookie_storage[record.Cookie] = txt; } } }
/// <summary> /// Adds a new session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to add.</param> public async Task AddSessionAsync(SessionRecord record) { var txt = PrimitiveSerializer.Serialize(record); using (await m_lock.LockAsync()) { if (!string.IsNullOrWhiteSpace(record.XSRFToken)) { if (m_xsrf_storage.ContainsKey(record.XSRFToken)) { throw new ArgumentException("Attempted to re-insert an entry into the XSRF token table"); } if (!string.IsNullOrWhiteSpace(record.Cookie) && m_cookie_storage.ContainsKey(record.Cookie)) { throw new ArgumentException("Attempted to re-insert an entry into the cookie token table"); } m_xsrf_storage[record.XSRFToken] = txt; m_cookie_storage[record.Cookie] = txt; } else { if (string.IsNullOrWhiteSpace(record.Cookie)) { throw new ArgumentException("Attempted to inser a record that has neither XSRF nor a cookie"); } if (m_cookie_storage.ContainsKey(record.Cookie)) { throw new ArgumentException("Attempted to re-insert an entry into the cookie token table"); } m_cookie_storage[record.Cookie] = txt; } } }
/// <summary> /// Gets a value indicating if the session is valid /// </summary> /// <returns><c>true</c>, if the session is valid, <c>false</c> otherwise.</returns> /// <param name="session">The session to validate.</param> public static bool IsNullOrExpired(this SessionRecord session) { return(session == null || session.Expires < DateTime.Now); }
/// <summary> /// Handles the request /// </summary> /// <returns>The awaitable task.</returns> /// <param name="context">The requests context.</param> public virtual async Task <bool> HandleAsync(IHttpContext context) { if (!ForceCheck && !string.IsNullOrWhiteSpace(context.Request.UserID)) { return(false); } var xsrf = context.Request.Headers[XSRFHeaderName]; var cookie = context.Request.Cookies[AuthSessionCookieName]; if (UseXSRFTokens && CheckXSRFToken && string.IsNullOrWhiteSpace(xsrf)) { return(SetXSRFError(context)); } if (UseXSRFTokens && CheckXSRFToken) { var session = await ShortTermStorage.GetSessionFromXSRFAsync(xsrf); if (Utility.IsNullOrExpired(session)) { return(SetXSRFError(context)); } if (string.IsNullOrWhiteSpace(cookie) || session.Cookie != cookie) { if (await LoginWithBasicAuth(context)) { return(false); } if (await PerformLongTermLogin(context)) { return(false); } // Check for a Hijack response if (context.Response.HasSentHeaders) { return(true); } return(SetLoginError(context)); } await RefreshSessionTokensAsync(context, session); context.Request.UserID = session.UserID; } else { SessionRecord session = null; if (!string.IsNullOrWhiteSpace(cookie)) { session = await ShortTermStorage.GetSessionFromCookieAsync(cookie); } if (Utility.IsNullOrExpired(session)) { if (await LoginWithBasicAuth(context)) { return(false); } if (await PerformLongTermLogin(context)) { return(false); } // Check for a Hijack response if (context.Response.HasSentHeaders) { return(true); } return(SetLoginError(context)); } await RefreshSessionTokensAsync(context, session); context.Request.UserID = session.UserID; } return(false); }
/// <summary> /// Performs all steps required to do a login /// </summary> /// <returns>An awaitable task.</returns> /// <param name="context">The http context.</param> /// <param name="userid">The user ID.</param> /// <param name="series">The long-term series</param> /// <param name="withlongterm">A value indicating if a long-term session should be created</param> protected virtual async Task PerformLoginAsync(IHttpContext context, string userid, string series, bool withlongterm) { var session = new SessionRecord(); // Re-use the XSRF if possible if (UseXSRFTokens) { var xsrf = context.Request.Headers[XSRFHeaderName]; if (!string.IsNullOrWhiteSpace(xsrf)) { var prev = await ShortTermStorage.GetSessionFromXSRFAsync(xsrf); if (!Utility.IsNullOrExpired(prev) && prev.UserID == userid && !string.IsNullOrWhiteSpace(userid)) { session = prev; } } } session.UserID = userid; session.Expires = DateTime.Now.AddSeconds(ShortTermExpirationSeconds); // If the connection is using SSL, require SSL for the cookie var usingssl = context.Request.SslProtocol != System.Security.Authentication.SslProtocols.None; if (UseXSRFTokens) { session.XSRFToken = session.XSRFToken ?? PRNG.GetRandomString(32); context.Response.AddCookie(XSRFCookieName, session.XSRFToken, expires: session.Expires, httponly: false, path: CookiePath, secure: usingssl); } if (UseLongTermCookieStorage && LongTermStorage != null && (!string.IsNullOrWhiteSpace(series) || withlongterm)) { var cookie = new LongTermCookie(); if (!string.IsNullOrWhiteSpace(series)) { cookie.Series = series; } var st = new LongTermToken() { UserID = userid, Expires = DateTime.Now.AddSeconds(LongTermDurationSeconds), Series = cookie.Series, Token = PBKDF2.CreatePBKDF2(cookie.Token) }; await LongTermStorage.AddOrUpdateLongTermLoginAsync(st); context.Response.AddCookie(AuthCookieName, cookie.ToString(), expires: st.Expires, httponly: true, path: CookiePath, secure: usingssl); } session.Cookie = PRNG.GetRandomString(32); context.Response.AddCookie(AuthSessionCookieName, session.Cookie, expires: session.Expires, httponly: true, path: CookiePath, secure: usingssl); await ShortTermStorage.AddSessionAsync(session); SetLoginSuccess(context); context.Request.UserID = userid; }
/// <summary> /// Performs all steps required to do a login /// </summary> /// <returns>An awaitable task.</returns> /// <param name="context">The http context.</param> /// <param name="userid">The user ID.</param> /// <param name="series">The long-term series</param> /// <param name="withlongterm">A value indicating if a long-term session should be created</param> protected virtual async Task PerformLoginAsync(IHttpContext context, string userid, string series, bool withlongterm) { var session = new SessionRecord(); // Re-use the XSRF if possible if (UseXSRFTokens) { var xsrf = context.Request.Headers[XSRFHeaderName]; if (!string.IsNullOrWhiteSpace(xsrf)) { var prev = await ShortTermStorage.GetSessionFromXSRFAsync(xsrf); if (prev != null) { // Remove the previous entry to avoid conflicts await ShortTermStorage.DropSessionAsync(prev); // Re-use the XSRF token session.XSRFToken = prev.XSRFToken; } } } session.UserID = userid; session.Expires = DateTime.Now.AddSeconds(ShortTermExpirationSeconds); // If the connection is using SSL, require SSL for the cookie var usingssl = context.Request.SslProtocol != System.Security.Authentication.SslProtocols.None; if (UseXSRFTokens) { session.XSRFToken = session.XSRFToken ?? PRNG.GetRandomString(32); context.Response.AddCookie(XSRFCookieName, session.XSRFToken, expires: session.Expires, httponly: false, path: CookiePath, secure: usingssl); } if (UseLongTermCookieStorage && LongTermStorage != null && (!string.IsNullOrWhiteSpace(series) || withlongterm)) { var cookie = new LongTermCookie(); if (!string.IsNullOrWhiteSpace(series)) { cookie.Series = series; } var st = new LongTermToken() { UserID = userid, Expires = DateTime.Now.AddSeconds(LongTermDurationSeconds), Series = cookie.Series, Token = PBKDF2.CreatePBKDF2(cookie.Token) }; await LongTermStorage.AddOrUpdateLongTermLoginAsync(st); context.Response.AddCookie(AuthCookieName, cookie.ToString(), expires: st.Expires, httponly: true, path: CookiePath, secure: usingssl); } session.Cookie = PRNG.GetRandomString(32); context.Response.AddCookie(AuthSessionCookieName, session.Cookie, expires: session.Expires, httponly: true, path: CookiePath, secure: usingssl); if (ShortTermStorage == null) { Console.WriteLine("Missing short term storage module, make sure you load Ceen.Security.Login.DatabaseStorageModule or manually set a storage module"); } await ShortTermStorage.AddSessionAsync(session); SetLoginSuccess(context); context.Request.UserID = userid; }
/// <summary> /// Adds a new session record /// </summary> /// <returns>An awaitable task.</returns> /// <param name="record">The record to add.</param> public virtual Task AddSessionAsync(SessionRecord record) { return(m_lock.LockedAsync(() => m_connection.InsertItem(record))); }