source: binary-improvements2/MapRendering/Web/OpenID.cs@ 384

Last change on this file since 384 was 382, checked in by alloc, 2 years ago

Switched to use SpaceWizards.HttpListener

File size: 7.7 KB
RevLine 
[244]1using System;
2using System.Collections.Generic;
3using System.Collections.Specialized;
4using System.IO;
5using System.Net;
6using System.Net.Security;
[325]7using System.Reflection;
8using System.Security.Cryptography.X509Certificates;
[244]9using System.Text;
10using System.Text.RegularExpressions;
[382]11using HttpListenerRequest = SpaceWizards.HttpListener.HttpListenerRequest;
[244]12
[325]13namespace AllocsFixes.NetConnections.Servers.Web {
[244]14 public static class OpenID {
15 private const string STEAM_LOGIN = "https://steamcommunity.com/openid/login";
16
[325]17 private static readonly Regex steamIdUrlMatcher =
18 new Regex (@"^https?:\/\/steamcommunity\.com\/openid\/id\/([0-9]{17,18})");
[314]19
[325]20 private static readonly X509Certificate2 caCert =
21 new X509Certificate2 (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
22 "/steam-rootca.cer");
23
24 private static readonly X509Certificate2 caIntermediateCert =
25 new X509Certificate2 (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
26 "/steam-intermediate.cer");
27
[351]28 private const bool verboseSsl = false;
[325]29 public static bool debugOpenId;
[314]30
[244]31 static OpenID () {
[325]32 for (int i = 0; i < Environment.GetCommandLineArgs ().Length; i++) {
33 if (Environment.GetCommandLineArgs () [i].EqualsCaseInsensitive ("-debugopenid")) {
[317]34 debugOpenId = true;
35 }
36 }
37
[351]38 ServicePointManager.ServerCertificateValidationCallback = (_srvPoint, _certificate, _chain, _errors) => {
39 if (_errors == SslPolicyErrors.None) {
[314]40 if (verboseSsl) {
41 Log.Out ("Steam certificate: No error (1)");
42 }
[325]43
[244]44 return true;
[314]45 }
[244]46
[314]47 X509Chain privateChain = new X509Chain ();
48 privateChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
[244]49
[314]50 privateChain.ChainPolicy.ExtraStore.Add (caCert);
51 privateChain.ChainPolicy.ExtraStore.Add (caIntermediateCert);
52
[351]53 if (privateChain.Build (new X509Certificate2 (_certificate))) {
[314]54 // No errors, immediately return
55 privateChain.Reset ();
56 if (verboseSsl) {
57 Log.Out ("Steam certificate: No error (2)");
58 }
[325]59
[314]60 return true;
61 }
62
63 if (privateChain.ChainStatus.Length == 0) {
64 // No errors, immediately return
65 privateChain.Reset ();
66 if (verboseSsl) {
67 Log.Out ("Steam certificate: No error (3)");
68 }
[325]69
[314]70 return true;
71 }
72
73 // Iterate all chain elements
74 foreach (X509ChainElement chainEl in privateChain.ChainElements) {
75 if (verboseSsl) {
76 Log.Out ("Validating cert: " + chainEl.Certificate.Subject);
77 }
[325]78
[314]79 // Iterate all status flags of the current cert
80 foreach (X509ChainStatus chainStatus in chainEl.ChainElementStatus) {
81 if (verboseSsl) {
82 Log.Out (" Status: " + chainStatus.Status);
83 }
[325]84
[314]85 if (chainStatus.Status == X509ChainStatusFlags.NoError) {
86 // This status is not an error, skip
87 continue;
88 }
[325]89
[326]90 if (chainStatus.Status == X509ChainStatusFlags.UntrustedRoot && chainEl.Certificate.Equals (caCert)) {
[314]91 // This status is about the cert being an untrusted root certificate but the certificate is one of those we added, ignore
92 continue;
93 }
[325]94
[314]95 // This status is an error, print information
[325]96 Log.Warning ("Steam certificate error: " + chainEl.Certificate.Subject + " ### Error: " +
97 chainStatus.Status);
[314]98 privateChain.Reset ();
99 return false;
100 }
101 }
102
103 foreach (X509ChainStatus chainStatus in privateChain.ChainStatus) {
[325]104 if (chainStatus.Status != X509ChainStatusFlags.NoError &&
105 chainStatus.Status != X509ChainStatusFlags.UntrustedRoot) {
[314]106 Log.Warning ("Steam certificate error: " + chainStatus.Status);
107 privateChain.Reset ();
108 return false;
109 }
110 }
111
112 // We didn't find any errors, chain is valid
113 privateChain.Reset ();
114 if (verboseSsl) {
115 Log.Out ("Steam certificate: No error (4)");
116 }
[325]117
[244]118 return true;
119 };
120 }
121
122 public static string GetOpenIdLoginUrl (string _returnHost, string _returnUrl) {
123 Dictionary<string, string> queryParams = new Dictionary<string, string> ();
124
125 queryParams.Add ("openid.ns", "http://specs.openid.net/auth/2.0");
126 queryParams.Add ("openid.mode", "checkid_setup");
127 queryParams.Add ("openid.return_to", _returnUrl);
128 queryParams.Add ("openid.realm", _returnHost);
129 queryParams.Add ("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select");
130 queryParams.Add ("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select");
131
132 return STEAM_LOGIN + '?' + buildUrlParams (queryParams);
133 }
134
135 public static ulong Validate (HttpListenerRequest _req) {
[316]136 string mode = getValue (_req, "openid.mode");
137 if (mode == "cancel") {
[244]138 Log.Warning ("Steam OpenID login canceled");
139 return 0;
140 }
[325]141
[316]142 if (mode == "error") {
143 Log.Warning ("Steam OpenID login error: " + getValue (_req, "openid.error"));
[317]144 if (debugOpenId) {
145 PrintOpenIdResponse (_req);
146 }
[325]147
[316]148 return 0;
149 }
[325]150
[244]151 string steamIdString = getValue (_req, "openid.claimed_id");
[326]152 ulong steamId;
[244]153 Match steamIdMatch = steamIdUrlMatcher.Match (steamIdString);
154 if (steamIdMatch.Success) {
155 steamId = ulong.Parse (steamIdMatch.Groups [1].Value);
156 } else {
157 Log.Warning ("Steam OpenID login result did not give a valid SteamID");
[317]158 if (debugOpenId) {
159 PrintOpenIdResponse (_req);
160 }
[325]161
[244]162 return 0;
163 }
164
165 Dictionary<string, string> queryParams = new Dictionary<string, string> ();
166
167 queryParams.Add ("openid.ns", "http://specs.openid.net/auth/2.0");
168
169 queryParams.Add ("openid.assoc_handle", getValue (_req, "openid.assoc_handle"));
170 queryParams.Add ("openid.signed", getValue (_req, "openid.signed"));
171 queryParams.Add ("openid.sig", getValue (_req, "openid.sig"));
172 queryParams.Add ("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select");
173 queryParams.Add ("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select");
174
175 string[] signeds = getValue (_req, "openid.signed").Split (',');
176 foreach (string s in signeds) {
177 queryParams ["openid." + s] = getValue (_req, "openid." + s);
178 }
179
180 queryParams.Add ("openid.mode", "check_authentication");
181
182 byte[] postData = Encoding.ASCII.GetBytes (buildUrlParams (queryParams));
[325]183 HttpWebRequest request = (HttpWebRequest) WebRequest.Create (STEAM_LOGIN);
[244]184 request.Method = "POST";
185 request.ContentType = "application/x-www-form-urlencoded";
186 request.ContentLength = postData.Length;
187 request.Headers.Add (HttpRequestHeader.AcceptLanguage, "en");
188 using (Stream st = request.GetRequestStream ()) {
189 st.Write (postData, 0, postData.Length);
190 }
191
[325]192 HttpWebResponse response = (HttpWebResponse) request.GetResponse ();
[326]193 string responseString;
[244]194 using (Stream st = response.GetResponseStream ()) {
195 using (StreamReader str = new StreamReader (st)) {
196 responseString = str.ReadToEnd ();
197 }
198 }
199
[326]200 if (responseString.ContainsCaseInsensitive ("is_valid:true")) {
[244]201 return steamId;
202 }
[325]203
204 Log.Warning ("Steam OpenID login failed: {0}", responseString);
205 return 0;
[244]206 }
207
208 private static string buildUrlParams (Dictionary<string, string> _queryParams) {
209 string[] paramsArr = new string[_queryParams.Count];
210 int i = 0;
211 foreach (KeyValuePair<string, string> kvp in _queryParams) {
212 paramsArr [i++] = kvp.Key + "=" + Uri.EscapeDataString (kvp.Value);
213 }
[325]214
[244]215 return string.Join ("&", paramsArr);
216 }
217
218 private static string getValue (HttpListenerRequest _req, string _name) {
219 NameValueCollection nvc = _req.QueryString;
220 if (nvc [_name] == null) {
221 throw new MissingMemberException ("OpenID parameter \"" + _name + "\" missing");
222 }
[325]223
[244]224 return nvc [_name];
225 }
226
[317]227 private static void PrintOpenIdResponse (HttpListenerRequest _req) {
228 NameValueCollection nvc = _req.QueryString;
229 for (int i = 0; i < nvc.Count; i++) {
230 Log.Out (" " + nvc.GetKey (i) + " = " + nvc [i]);
231 }
232 }
[244]233 }
[325]234}
Note: See TracBrowser for help on using the repository browser.