summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OAuth.Consumer/OAuth/CookieTemporaryCredentialStorage.cs
blob: 25941e69d0450574191f6ff3748ee48975c19aba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//-----------------------------------------------------------------------
// <copyright file="CookieTemporaryCredentialStorage.cs" company="Microsoft">
//     Copyright (c) Microsoft. All rights reserved.
//     Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

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;

	/// <summary>
	/// Provides temporary credential storage by persisting them in a protected cookie on the
	/// user agent (i.e. browser).
	/// </summary>
	public class CookieTemporaryCredentialStorage : ITemporaryCredentialStorage {
		/// <summary>
		/// Key used for token cookie
		/// </summary>
		protected const string TokenCookieKey = "DNOAOAuth1TempCredential";

		/// <summary>
		/// Primary request context.
		/// </summary>
		private readonly HttpContextBase httpContext;

		/// <summary>
		/// Initializes a new instance of the <see cref="CookieTemporaryCredentialsStorage"/> class
		/// using <see cref="HttpContext.Current"/> as the source for the context to read and write cookies to.
		/// </summary>
		public CookieTemporaryCredentialStorage()
			: this(new HttpContextWrapper(HttpContext.Current)) {
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="CookieTemporaryCredentialsStorage"/> class.
		/// </summary>
		/// <param name="httpContext">The HTTP context from and to which to access cookies.</param>
		public CookieTemporaryCredentialStorage(HttpContextBase httpContext) {
			Requires.NotNull(httpContext, "httpContext");
			this.httpContext = httpContext;
		}

		#region ITemporaryCredentialsStorage Members

		/// <summary>
		/// Saves the temporary credential.
		/// </summary>
		/// <param name="identifier">The identifier.</param>
		/// <param name="secret">The secret.</param>
		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);
			cookie.Values[identifier] = encryptedToken;

			this.httpContext.Response.Cookies.Set(cookie);
		}

		/// <summary>
		/// Obtains the temporary credential identifier and secret, if available.
		/// </summary>
		/// <returns>
		/// An initialized key value pair if credentials are available; otherwise both key and value are <c>null</c>.
		/// </returns>
		/// <exception cref="System.NotImplementedException"></exception>
		public KeyValuePair<string, string> RetrieveTemporaryCredential() {
			HttpCookie cookie = this.httpContext.Request.Cookies[TokenCookieKey];
			if (cookie == null || cookie.Values.Count == 0) {
				return new KeyValuePair<string, string>();
			}

			string identifier = cookie.Values.GetKey(0);
			string secret = DecodeAndUnprotectToken(identifier, cookie.Values[identifier]);
			return new KeyValuePair<string, string>(identifier, secret);
		}

		/// <summary>
		/// Clears the temporary credentials from storage.
		/// </summary>
		/// <remarks>
		/// DotNetOpenAuth calls this when the credentials are no longer needed.
		/// </remarks>
		public void ClearTemporaryCredential() {
			var cookie = new HttpCookie(TokenCookieKey) {
				Value = string.Empty,
				Expires = DateTime.UtcNow.AddDays(-5),
			};
			this.httpContext.Response.Cookies.Set(cookie);
		}

		#endregion

		/// <summary>
		/// Protect and url-encode the specified token secret.
		/// </summary>
		/// <param name="token">The token to be used as a key.</param>
		/// <param name="tokenSecret">The token secret to be protected</param>
		/// <returns>The encrypted and protected string.</returns>
		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);
		}

		/// <summary>
		/// Url-decode and unprotect the specified encrypted token string.
		/// </summary>
		/// <param name="token">The token to be used as a key.</param>
		/// <param name="encryptedToken">The encrypted token to be decrypted</param>
		/// <returns>The original token secret</returns>
		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);
		}
	}
}