//----------------------------------------------------------------------- // // Copyright (c) Microsoft. All rights reserved. // Copyright (c) Andrew Arnott. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Security; using Validation; /// /// Provides temporary credential storage by persisting them in a protected cookie on the /// user agent (i.e. browser). /// public class CookieTemporaryCredentialStorage : ITemporaryCredentialStorage { /// /// Key used for token cookie /// protected const string TokenCookieKey = "DNOAOAuth1TempCredential"; /// /// Primary request context. /// private readonly HttpContextBase httpContext; /// /// Initializes a new instance of the class /// using as the source for the context to read and write cookies to. /// public CookieTemporaryCredentialStorage() : this(new HttpContextWrapper(HttpContext.Current)) { } /// /// Initializes a new instance of the class. /// /// The HTTP context from and to which to access cookies. public CookieTemporaryCredentialStorage(HttpContextBase httpContext) { Requires.NotNull(httpContext, "httpContext"); this.httpContext = httpContext; } #region ITemporaryCredentialsStorage Members /// /// Saves the temporary credential. /// /// The identifier. /// The secret. public void SaveTemporaryCredential(string identifier, string secret) { var cookie = new HttpCookie(TokenCookieKey) { HttpOnly = true }; if (FormsAuthentication.RequireSSL) { cookie.Secure = true; } var encryptedToken = ProtectAndEncodeToken(identifier, secret); var escapedIdentifier = Uri.EscapeDataString(identifier); cookie.Values[escapedIdentifier] = encryptedToken; this.httpContext.Response.Cookies.Set(cookie); } /// /// Obtains the temporary credential identifier and secret, if available. /// /// /// An initialized key value pair if credentials are available; otherwise both key and value are null. /// public KeyValuePair RetrieveTemporaryCredential() { HttpCookie cookie = this.httpContext.Request.Cookies[TokenCookieKey]; if (cookie == null || cookie.Values.Count == 0) { return new KeyValuePair(); } string escapedIdentifier = cookie.Values.GetKey(0); string identifier = Uri.UnescapeDataString(escapedIdentifier); string secret = DecodeAndUnprotectToken(identifier, cookie.Values[escapedIdentifier]); return new KeyValuePair(identifier, secret); } /// /// Clears the temporary credentials from storage. /// /// /// DotNetOpenAuth calls this when the credentials are no longer needed. /// public void ClearTemporaryCredential() { var cookie = new HttpCookie(TokenCookieKey) { Value = string.Empty, Expires = DateTime.UtcNow.AddDays(-5), }; this.httpContext.Response.Cookies.Set(cookie); } #endregion /// /// Protect and url-encode the specified token secret. /// /// The token to be used as a key. /// The token secret to be protected /// The encrypted and protected string. protected static string ProtectAndEncodeToken(string token, string tokenSecret) { byte[] cookieBytes = Encoding.UTF8.GetBytes(tokenSecret); var secretBytes = MachineKeyUtil.Protect(cookieBytes, TokenCookieKey, "Token:" + token); return HttpServerUtility.UrlTokenEncode(secretBytes); } /// /// Url-decode and unprotect the specified encrypted token string. /// /// The token to be used as a key. /// The encrypted token to be decrypted /// The original token secret protected static string DecodeAndUnprotectToken(string token, string encryptedToken) { byte[] cookieBytes = HttpServerUtility.UrlTokenDecode(encryptedToken); byte[] clearBytes = MachineKeyUtil.Unprotect(cookieBytes, TokenCookieKey, "Token:" + token); return Encoding.UTF8.GetString(clearBytes); } } }