diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-19 17:27:18 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-19 17:27:18 -0700 |
commit | 82c34d3f7e46f113b1c13145da0c388d9c328215 (patch) | |
tree | 18312208d8d0b08f3aa055a573116c33a268bd37 | |
parent | c071c9469d444ba356840b4ae0bc07b617147480 (diff) | |
parent | 42ec238847675f5943468fc6fc22b0e0bbf43f1c (diff) | |
download | DotNetOpenAuth-82c34d3f7e46f113b1c13145da0c388d9c328215.zip DotNetOpenAuth-82c34d3f7e46f113b1c13145da0c388d9c328215.tar.gz DotNetOpenAuth-82c34d3f7e46f113b1c13145da0c388d9c328215.tar.bz2 |
Merge branch 'v3.0' into v3.1
Conflicts:
src/DotNetOpenAuth.sln
src/DotNetOpenAuth.vsmdi
src/DotNetOpenAuth/DotNetOpenAuth.csproj
src/DotNetOpenAuth/OAuth/ConsumerBase.cs
src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
src/DotNetOpenAuth/OAuth/OAuthStrings.resx
src/DotNetOpenAuth/Yadis/Yadis.cs
90 files changed, 8108 insertions, 503 deletions
diff --git a/doc/specs/OAuth Core 1.0a (Draft 3).htm b/doc/specs/OAuth Core 1.0a (Draft 3).htm new file mode 100644 index 0000000..9b8de42 --- /dev/null +++ b/doc/specs/OAuth Core 1.0a (Draft 3).htm @@ -0,0 +1,2477 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html lang="en"><head><title>Implementer Draft: OAuth Core 1.0 Rev A (Draft 3)</title> +<meta http-equiv="Expires" content="Tue, 12 May 2009 22:56:13 +0000"> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<meta name="description" content="OAuth Core 1.0 Rev A (Draft 3)"> +<meta name="generator" content="xml2rfc v1.34pre3 (http://xml.resource.org/)"> +<style type='text/css'><!-- + body { + font-family: verdana, charcoal, helvetica, arial, sans-serif; + font-size: small; color: #000; background-color: #FFF; + margin: 2em; + } + h1, h2, h3, h4, h5, h6 { + font-family: helvetica, monaco, "MS Sans Serif", arial, sans-serif; + font-weight: bold; font-style: normal; + } + h1 { color: #900; background-color: transparent; text-align: right; } + h3 { color: #333; background-color: transparent; } + + td.RFCbug { + font-size: x-small; text-decoration: none; + width: 30px; height: 30px; padding-top: 2px; + text-align: justify; vertical-align: middle; + background-color: #000; + } + td.RFCbug span.RFC { + font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, verdana, sans-serif; + font-weight: bold; color: #666; + } + td.RFCbug span.hotText { + font-family: charcoal, monaco, geneva, "MS Sans Serif", helvetica, verdana, sans-serif; + font-weight: normal; text-align: center; color: #FFF; + } + + table.TOCbug { width: 30px; height: 15px; } + td.TOCbug { + text-align: center; width: 30px; height: 15px; + color: #FFF; background-color: #900; + } + td.TOCbug a { + font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, sans-serif; + font-weight: bold; font-size: x-small; text-decoration: none; + color: #FFF; background-color: transparent; + } + + td.header { + font-family: arial, helvetica, sans-serif; font-size: x-small; + vertical-align: top; width: 33%; + color: #FFF; background-color: #666; + } + td.author { font-weight: bold; font-size: x-small; margin-left: 4em; } + td.author-text { font-size: x-small; } + + /* info code from SantaKlauss at http://www.madaboutstyle.com/tooltip2.html */ + a.info { + /* This is the key. */ + position: relative; + z-index: 24; + text-decoration: none; + } + a.info:hover { + z-index: 25; + color: #FFF; background-color: #900; + } + a.info span { display: none; } + a.info:hover span.info { + /* The span will display just on :hover state. */ + display: block; + position: absolute; + font-size: smaller; + top: 2em; left: -5em; width: 15em; + padding: 2px; border: 1px solid #333; + color: #900; background-color: #EEE; + text-align: left; + } + + a { font-weight: bold; } + a:link { color: #900; background-color: transparent; } + a:visited { color: #633; background-color: transparent; } + a:active { color: #633; background-color: transparent; } + + p { margin-left: 2em; margin-right: 2em; } + p.copyright { font-size: x-small; } + p.toc { font-size: small; font-weight: bold; margin-left: 3em; } + table.toc { margin: 0 0 0 3em; padding: 0; border: 0; vertical-align: text-top; } + td.toc { font-size: small; font-weight: bold; vertical-align: text-top; } + + ol.text { margin-left: 2em; margin-right: 2em; } + ul.text { margin-left: 2em; margin-right: 2em; } + li { margin-left: 3em; } + + /* RFC-2629 <spanx>s and <artwork>s. */ + em { font-style: italic; } + strong { font-weight: bold; } + dfn { font-weight: bold; font-style: normal; } + cite { font-weight: normal; font-style: normal; } + tt { color: #036; } + tt, pre, pre dfn, pre em, pre cite, pre span { + font-family: "Courier New", Courier, monospace; font-size: small; + } + pre { + text-align: left; padding: 4px; + color: #000; background-color: #CCC; + } + pre dfn { color: #900; } + pre em { color: #66F; background-color: #FFC; font-weight: normal; } + pre .key { color: #33C; font-weight: bold; } + pre .id { color: #900; } + pre .str { color: #000; background-color: #CFF; } + pre .val { color: #066; } + pre .rep { color: #909; } + pre .oth { color: #000; background-color: #FCF; } + pre .err { background-color: #FCC; } + + /* RFC-2629 <texttable>s. */ + table.all, table.full, table.headers, table.none { + font-size: small; text-align: center; border-width: 2px; + vertical-align: top; border-collapse: collapse; + } + table.all, table.full { border-style: solid; border-color: black; } + table.headers, table.none { border-style: none; } + th { + font-weight: bold; border-color: black; + border-width: 2px 2px 3px 2px; + } + table.all th, table.full th { border-style: solid; } + table.headers th { border-style: none none solid none; } + table.none th { border-style: none; } + table.all td { + border-style: solid; border-color: #333; + border-width: 1px 2px; + } + table.full td, table.headers td, table.none td { border-style: none; } + + hr { height: 1px; } + hr.insert { + width: 80%; border-style: none; border-width: 0; + color: #CCC; background-color: #CCC; + } +--></style> +</head> +<body> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<table summary="layout" width="66%" border="0" cellpadding="0" cellspacing="0"><tr><td><table summary="layout" width="100%" border="0" cellpadding="2" cellspacing="1"> +<tr><td class="header">Implementer Draft</td><td class="header"> OAuth</td></tr> +<tr><td class="header"> </td><td class="header">May 12, 2009</td></tr> +</table></td></tr></table> +<h1><br />OAuth Core 1.0 Rev A (Draft 3)</h1> + +<h3>Abstract</h3> + +<p> + The OAuth protocol enables websites or applications (Consumers) to + access Protected Resources from a web service (Service Provider) via an + API, without requiring Users to disclose their Service Provider + credentials to the Consumers. More generally, OAuth creates a + freely-implementable and generic methodology for API authentication. + +</p> +<p> + An example use case is allowing printing service printer.example.com + (the Consumer), to access private photos stored on photos.example.net + (the Service Provider) without requiring Users to provide their + photos.example.net credentials to printer.example.com. + +</p> +<p> + OAuth does not require a specific user interface or interaction + pattern, nor does it specify how Service Providers authenticate Users, + making the protocol ideally suited for cases where authentication + credentials are unavailable to the Consumer, such as with OpenID. + +</p> +<p> + OAuth aims to unify the experience and implementation of delegated web + service authentication into a single, community-driven protocol. OAuth + builds on existing protocols and best practices that have been + independently implemented by various websites. An open standard, + supported by large and small providers alike, promotes a consistent and + trusted experience for both application developers and the users of + those applications. + +</p><a name="toc"></a><br /><hr /> +<h3>Table of Contents</h3> +<p class="toc"> +<a href="#anchor1">1.</a> +Authors<br /> +<a href="#anchor2">2.</a> +Notation and Conventions<br /> +<a href="#anchor3">3.</a> +Definitions<br /> +<a href="#anchor4">4.</a> +Documentation and Registration<br /> + <a href="#request_urls">4.1.</a> +Request URLs<br /> + <a href="#anchor5">4.2.</a> +Service Providers<br /> + <a href="#anchor6">4.3.</a> +Consumers<br /> +<a href="#anchor7">5.</a> +Parameters<br /> + <a href="#encoding_parameters">5.1.</a> +Parameter Encoding<br /> + <a href="#consumer_req_param">5.2.</a> +Consumer Request Parameters<br /> + <a href="#response_parameters">5.3.</a> +Service Provider Response Parameters<br /> + <a href="#auth_header">5.4.</a> +OAuth HTTP Authorization Scheme<br /> +<a href="#anchor9">6.</a> +Authenticating with OAuth<br /> + <a href="#auth_step1">6.1.</a> +Obtaining an Unauthorized Request Token<br /> + <a href="#auth_step2">6.2.</a> +Obtaining User Authorization<br /> + <a href="#auth_step3">6.3.</a> +Obtaining an Access Token<br /> +<a href="#anchor12">7.</a> +Accessing Protected Resources<br /> +<a href="#nonce">8.</a> +Nonce and Timestamp<br /> +<a href="#signing_process">9.</a> +Signing Requests<br /> + <a href="#anchor13">9.1.</a> +Signature Base String<br /> + <a href="#anchor15">9.2.</a> +HMAC-SHA1<br /> + <a href="#anchor18">9.3.</a> +RSA-SHA1<br /> + <a href="#anchor21">9.4.</a> +PLAINTEXT<br /> +<a href="#http_codes">10.</a> +HTTP Response Codes<br /> +<a href="#anchor24">11.</a> +Security Considerations<br /> + <a href="#anchor25">11.1.</a> +Credentials and Token Exchange<br /> + <a href="#anchor26">11.2.</a> +PLAINTEXT Signature Method<br /> + <a href="#anchor27">11.3.</a> +Confidentiality of Requests<br /> + <a href="#anchor28">11.4.</a> +Spoofing by Counterfeit Servers<br /> + <a href="#anchor29">11.5.</a> +Proxying and Caching of Authenticated Content<br /> + <a href="#anchor30">11.6.</a> +Plaintext Storage of Credentials<br /> + <a href="#anchor31">11.7.</a> +Secrecy of the Consumer Secret<br /> + <a href="#anchor32">11.8.</a> +Phishing Attacks<br /> + <a href="#anchor33">11.9.</a> +Scoping of Access Requests<br /> + <a href="#anchor34">11.10.</a> +Entropy of Secrets<br /> + <a href="#anchor35">11.11.</a> +Denial of Service / Resource Exhaustion Attacks<br /> + <a href="#anchor36">11.12.</a> +Cryptographic Attacks<br /> + <a href="#anchor37">11.13.</a> +Signature Base String Compatibility<br /> + <a href="#anchor38">11.14.</a> +Cross-Site Request Forgery (CSRF)<br /> + <a href="#anchor39">11.15.</a> +User Interface Redress<br /> + <a href="#anchor40">11.16.</a> +Automatic Processing of Repeat Authorizations<br /> +<a href="#anchor41">Appendix A.</a> +Appendix A - Protocol Example<br /> +<a href="#anchor42">Appendix A.1.</a> +Documentation and Registration<br /> +<a href="#anchor43">Appendix A.2.</a> +Obtaining a Request Token<br /> +<a href="#anchor44">Appendix A.3.</a> +Requesting User Authorization<br /> +<a href="#anchor45">Appendix A.4.</a> +Obtaining an Access Token<br /> +<a href="#anchor46">Appendix A.5.</a> +Accessing Protected Resources<br /> +<a href="#rfc.references1">12.</a> +References<br /> +<a href="#rfc.authors">§</a> +Author's Address<br /> +</p> +<br clear="all" /> + +<a name="anchor1"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.1"></a><h3>1. +Authors</h3> + +<p> + </p> +<blockquote class="text"> +<p>Mark Atwood (me@mark.atwood.name) +</p> +<p>Dirk Balfanz (balfanz@google.com) +</p> +<p>Darren Bounds (darren@cliqset.com) +</p> +<p>Richard M. Conlan (zeveck@google.com) +</p> +<p>Blaine Cook (blaine@twitter.com) +</p> +<p>Leah Culver (leah@pownce.com) +</p> +<p>Breno de Medeiros (breno@google.com) +</p> +<p>Brian Eaton (beaton@google.com) +</p> +<p>Kellan Elliott-McCrea (kellan@flickr.com) +</p> +<p>Larry Halff (larry@ma.gnolia.com) +</p> +<p>Eran Hammer-Lahav (eran@hueniverse.com), Editor +</p> +<p>Ben Laurie (benl@google.com) +</p> +<p>Chris Messina (chris@citizenagency.com) +</p> +<p>John Panzer (jpanzer@acm.org) +</p> +<p>Sam Quigley (quigley@emerose.com) +</p> +<p>David Recordon (david@sixapart.com) +</p> +<p>Eran Sandler (eran@yedda.com) +</p> +<p>Jonathan Sergent (sergent@google.com) +</p> +<p>Todd Sieling (todd@ma.gnolia.com) +</p> +<p>Brian Slesinsky (brian-oauth@slesinsky.org) +</p> +<p>Andy Smith (andy@jaiku.com) +</p> +</blockquote><p> + +</p> +<a name="anchor2"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.2"></a><h3>2. +Notation and Conventions</h3> + +<p> + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in <a class='info' href='#RFC2119'>[RFC2119]<span> (</span><span class='info'>Bradner, B., “Key words for use in RFCs to Indicate Requirement Levels,” .</span><span>)</span></a>. + Domain name examples use <a class='info' href='#RFC2606'>[RFC2606]<span> (</span><span class='info'>Eastlake, D. and A. Panitz, “Reserved Top Level DNS Names,” .</span><span>)</span></a>. + +</p> +<a name="anchor3"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.3"></a><h3>3. +Definitions</h3> + +<p> + </p> +<blockquote class="text"><dl> +<dt>Service Provider:</dt> +<dd> + A web application that allows access via OAuth. + +</dd> +<dt>User:</dt> +<dd> + An individual who has an account with the Service Provider. + +</dd> +<dt>Consumer:</dt> +<dd> + A website or application that uses OAuth to access the Service + Provider on behalf of the User. + +</dd> +<dt>Protected Resource(s):</dt> +<dd> + Data controlled by the Service Provider, which the Consumer can + access through authentication. + +</dd> +<dt>Consumer Developer:</dt> +<dd> + An individual or organization that implements a Consumer. + +</dd> +<dt>Consumer Key:</dt> +<dd> + A value used by the Consumer to identify itself to the Service + Provider. + +</dd> +<dt>Consumer Secret:</dt> +<dd> + A secret used by the Consumer to establish ownership of the + Consumer Key. + +</dd> +<dt>Request Token:</dt> +<dd> + A value used by the Consumer to obtain authorization from the User, + and exchanged for an Access Token. + +</dd> +<dt>Access Token:</dt> +<dd> + A value used by the Consumer to gain access to the Protected + Resources on behalf of the User, instead of using the User's + Service Provider credentials. + +</dd> +<dt>Token Secret:</dt> +<dd> + A secret used by the Consumer to establish ownership of a given + Token. + +</dd> +<dt>OAuth Protocol Parameters:</dt> +<dd> + Parameters with names beginning with <tt>oauth_</tt>. + +</dd> +</dl></blockquote><p> + +</p> +<a name="anchor4"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.4"></a><h3>4. +Documentation and Registration</h3> + +<p> + OAuth includes a Consumer Key and matching Consumer Secret that + together authenticate the Consumer (as opposed to the User) to the + Service Provider. Consumer-specific identification allows the Service + Provider to vary access levels to Consumers (such as un-throttled access + to resources). + +</p> +<p> + Service Providers SHOULD NOT rely on the Consumer Secret as a method to + verify the Consumer identity, unless the Consumer Secret is known to be + inaccessible to anyone other than the Consumer and the Service + Provider. The Consumer Secret MAY be an empty string (for example when + no Consumer verification is needed, or when verification is achieved + through other means such as RSA). + +</p> +<a name="request_urls"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.4.1"></a><h3>4.1. +Request URLs</h3> + +<p> + OAuth defines three request URLs: + + </p> +<blockquote class="text"><dl> +<dt>Request Token URL:</dt> +<dd> + The URL used to obtain an unauthorized Request Token, described + in <a class='info' href='#auth_step1'>Section 6.1<span> (</span><span class='info'>Obtaining an Unauthorized Request Token</span><span>)</span></a>. + +</dd> +<dt>User Authorization URL:</dt> +<dd> + The URL used to obtain User authorization for Consumer access, + described in <a class='info' href='#auth_step2'>Section 6.2<span> (</span><span class='info'>Obtaining User Authorization</span><span>)</span></a>. + +</dd> +<dt>Access Token URL:</dt> +<dd> + The URL used to exchange the User-authorized Request Token for + an Access Token, described in <a class='info' href='#auth_step3'>Section 6.3<span> (</span><span class='info'>Obtaining an Access Token</span><span>)</span></a>. + +</dd> +</dl></blockquote><p> + +</p> +<p> + The three URLs MUST include scheme, authority, and path, and MAY + include query and fragment as defined by <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> + section 3. The request URL query MUST NOT contain any OAuth Protocol + Parameters. For example: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://sp.example.com/authorize +</pre></div><p> + + +</p> +<a name="anchor5"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.4.2"></a><h3>4.2. +Service Providers</h3> + +<p> + The Service Provider's responsibility is to enable Consumer Developers + to establish a Consumer Key and Consumer Secret. The process and + requirements for provisioning these are entirely up to the Service + Providers. + +</p> +<p> + The Service Provider's documentation includes: + + </p> +<ol class="text"> +<li> + The <a class='info' href='#request_urls'>URLs<span> (</span><span class='info'>Request URLs</span><span>)</span></a> the Consumer will + use when making OAuth requests, and the HTTP methods (i.e. GET, + POST, etc.) used in the Request Token URL and Access Token URL. + +</li> +<li> + Signature methods supported by the Service Provider. + +</li> +<li> + Any additional request parameters that the Service Provider + requires in order to obtain a Token. Service Provider specific + parameters MUST NOT begin with <tt>oauth_</tt>. + +</li> +</ol><p> + +</p> +<a name="anchor6"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.4.3"></a><h3>4.3. +Consumers</h3> + +<p> + The Consumer Developer MUST establish a Consumer Key and a Consumer + Secret with the Service Provider. The Consumer Developer MAY also be + required to provide additional information to the Service Provider + upon registration. + +</p> +<a name="anchor7"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5"></a><h3>5. +Parameters</h3> + +<p> + OAuth Protocol Parameter names and values are case sensitive. Each + OAuth Protocol Parameters MUST NOT appear more than once per request, + and are REQUIRED unless otherwise noted. + +</p> +<a name="encoding_parameters"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.1"></a><h3>5.1. +Parameter Encoding</h3> + +<p> + All parameter names and values are escaped using the + <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> percent-encoding (%xx) mechanism. + Characters not in the unreserved character set + (<a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> section 2.3) MUST be encoded. Characters + in the unreserved character set MUST NOT be encoded. Hexadecimal + characters in encodings MUST be upper case. Text names and values + MUST be encoded as UTF-8 octets before percent-encoding them per + <a class='info' href='#RFC3629'>[RFC3629]<span> (</span><span class='info'>Yergeau, F., “UTF-8, a transformation format of Unicode and ISO 10646,” .</span><span>)</span></a>. + +</p><div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + unreserved = ALPHA, DIGIT, '-', '.', '_', '~' +</pre></div> +<a name="consumer_req_param"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.2"></a><h3>5.2. +Consumer Request Parameters</h3> + +<p> + OAuth Protocol Parameters are sent from the Consumer to the Service + Provider in one of three methods, in order of decreasing preference: + </p> +<ol class="text"> +<li> + In the HTTP <tt>Authorization</tt> header as defined in + <a class='info' href='#auth_header'>OAuth HTTP Authorization Scheme<span> (</span><span class='info'>OAuth HTTP Authorization Scheme</span><span>)</span></a>. + +</li> +<li> + As the HTTP POST request body with a <tt> + content-type + </tt> of + <tt>application/x-www-form-urlencoded</tt>. + +</li> +<li> + Added to the URLs in the query part (as defined by + <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> section 3). + +</li> +</ol><p> + +</p> +<p> + In addition to these defined methods, future extensions may describe + alternate methods for sending the OAuth Protocol Parameters. + The methods for sending other request parameters are left + undefined, but SHOULD NOT use the + <a class='info' href='#auth_header'>OAuth HTTP Authorization Scheme<span> (</span><span class='info'>OAuth HTTP Authorization Scheme</span><span>)</span></a> header. + +</p> +<a name="response_parameters"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.3"></a><h3>5.3. +Service Provider Response Parameters</h3> + +<p> + Response parameters are sent by the Service + Provider to return Tokens and other information to the Consumer in + the HTTP response body. The parameter names and values are first + encoded as per <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>, and concatenated with the '&' character (ASCII code 38) + as defined in <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> Section 2.1. For example: + +</p><div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + oauth_token=ab3cd9j4ks73hf7g&oauth_token_secret=xyz4992k83j47x0b +</pre></div> +<a name="auth_header"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.4"></a><h3>5.4. +OAuth HTTP Authorization Scheme</h3> + +<p> + This section defines an <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a> extension to + support OAuth. It uses the standard HTTP <tt>Authorization</tt> and + <tt>WWW-Authenticate</tt> headers to pass OAuth Protocol Parameters. + +</p> +<p> + It is RECOMMENDED that Service Providers accept the HTTP + <tt>Authorization</tt> header. Consumers SHOULD be able to send OAuth + Protocol Parameters in the OAuth <tt>Authorization</tt> header. + +</p> +<p> + The extension auth-scheme (as defined by + <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a>) is <tt>OAuth</tt> and is case-insensitive. + +</p> +<a name="auth_header_authorization"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.4.1"></a><h3>5.4.1. +Authorization Header</h3> + +<p> + The OAuth Protocol Parameters are sent in the <tt>Authorization</tt> + header the following way: + + </p> +<ol class="text"> +<li> + Parameter names and values are encoded per + <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>. + +</li> +<li> + For each parameter, the name is immediately followed by an '=' + character (ASCII code 61), a '"' character (ASCII code 34), the + parameter value (MAY be empty), and another '"' character + (ASCII code 34). + +</li> +<li> + Parameters are separated by a comma character (ASCII code 44) + and OPTIONAL linear whitespace per <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a>. + +</li> +<li> + The OPTIONAL <tt>realm</tt> parameter is added and interpreted per + <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a>, section 1.2. + +</li> +</ol><p> + +</p> +<p> + For example: + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + Authorization: OAuth realm="http://sp.example.com/", + oauth_consumer_key="0685bd9184jfhq22", + oauth_token="ad180jjd733klru7", + oauth_signature_method="HMAC-SHA1", + oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + oauth_timestamp="137131200", + oauth_nonce="4572616e48616d6d65724c61686176", + oauth_version="1.0" +</pre></div><p> + + +</p> +<a name="anchor8"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.5.4.2"></a><h3>5.4.2. +WWW-Authenticate Header</h3> + +<p> + Service Providers MAY indicate their support for the extension by + returning the OAuth HTTP <tt>WWW-Authenticate</tt> + header upon Consumer requests for Protected Resources. As per + <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a> such a response MAY include additional + HTTP <tt>WWW-Authenticate</tt> headers: + +</p> +<p> + For example: + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + WWW-Authenticate: OAuth realm="http://sp.example.com/" +</pre></div><p> + + +</p> +<p> + The realm parameter defines a protection realm per + <a class='info' href='#RFC2617'>[RFC2617]<span> (</span><span class='info'>Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “HTTP Authentication: Basic and Digest Access Authentication,” .</span><span>)</span></a>, section 1.2. + +</p> +<a name="anchor9"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6"></a><h3>6. +Authenticating with OAuth</h3> + +<p> + OAuth authentication is the process in which Users grant access to + their Protected Resources without sharing their credentials with the + Consumer. OAuth uses Tokens generated by the Service Provider instead + of the User's credentials in Protected Resources requests. The process + uses two Token types: + + </p> +<blockquote class="text"><dl> +<dt>Request Token:</dt> +<dd> + Used by the Consumer to ask the User to authorize access to the + Protected Resources. The User-authorized Request Token is exchanged + for an Access Token, MUST only be used once, and MUST NOT be used + for any other purpose. It is RECOMMENDED that Request Tokens have + a limited lifetime. + +</dd> +<dt>Access Token:</dt> +<dd> + Used by the Consumer to access the Protected Resources on behalf of + the User. Access Tokens MAY limit access to certain Protected + Resources, and MAY have a limited lifetime. Service Providers + SHOULD allow Users to revoke Access Tokens. Only the Access Token + SHALL be used to access the Protect Resources. + +</dd> +</dl></blockquote><p> + +</p> +<p> + OAuth Authentication is done in three steps: + + </p> +<ol class="text"> +<li> + The Consumer obtains an unauthorized Request Token. + +</li> +<li> + The User authorizes the Request Token. + +</li> +<li> + The Consumer exchanges the Request Token for an Access Token. + +</li> +</ol><p> + +</p> +<a name="auth_step1"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.1"></a><h3>6.1. +Obtaining an Unauthorized Request Token</h3> + +<p> + The Consumer obtains an unauthorized Request Token by asking the + Service Provider to issue a Token. The Request Token's sole purpose + is to receive User approval and can only be used to obtain an Access + Token. The Request Token process goes as follows: + +</p> +<a name="obtain_request_token"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.1.1"></a><h3>6.1.1. +Consumer Obtains a Request Token</h3> + +<p> + To obtain a Request Token, the Consumer sends an HTTP request to + the Service Provider's Request Token URL. The Service Provider + documentation specifies the HTTP method for this request, and HTTP POST + is RECOMMENDED. The request MUST be signed and contains the following parameters: + + </p> +<blockquote class="text"><dl> +<dt>oauth_consumer_key:</dt> +<dd> + The Consumer Key. + +</dd> +<dt>oauth_signature_method:</dt> +<dd> + The signature method the Consumer used to sign the request. + +</dd> +<dt>oauth_signature:</dt> +<dd> + The signature as defined in + <a class='info' href='#signing_process'>Signing Requests<span> (</span><span class='info'>Signing Requests</span><span>)</span></a>. + +</dd> +<dt>oauth_timestamp:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_nonce:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_version:</dt> +<dd> + OPTIONAL. If present, value MUST be <tt> + 1.0 + </tt>. Service Providers + MUST assume the protocol version to be <tt>1.0</tt> if this parameter + is not present. Service Providers' response to non-<tt>1.0</tt> value + is left undefined. + +</dd> +<dt>oauth_callback:</dt> +<dd> + An absolute URL to which the Service Provider will redirect the User back when the + <a class='info' href='#auth_step2'>Obtaining User Authorization<span> (</span><span class='info'>Obtaining User Authorization</span><span>)</span></a> step is completed. If the + Consumer is unable to receive callbacks or a callback URL has been established via other + means, the parameter value MUST be set to <tt>oob</tt> (case sensitive), + to indicate an out-of-band configuration. + +</dd> +<dt>Additional parameters:</dt> +<dd> + Any additional parameters, as defined by the Service Provider. + +</dd> +</dl></blockquote><p> + +</p> +<a name="request_grant"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.1.2"></a><h3>6.1.2. +Service Provider Issues an Unauthorized Request Token</h3> + +<p> + The Service Provider verifies the signature and Consumer Key. If + successful, it generates a Request Token and Token Secret and + returns them to the Consumer in the HTTP response body as defined + in <a class='info' href='#response_parameters'>Service Provider Response Parameters<span> (</span><span class='info'>Service Provider Response Parameters</span><span>)</span></a>. + The Service Provider MUST ensure the Request + Token cannot be exchanged for an Access Token until the User + successfully grants access in <a class='info' href='#auth_step2'>Obtaining + User Authorization<span> (</span><span class='info'>Obtaining User Authorization</span><span>)</span></a>. + +</p> +<p> + The response contains the following parameters: + + </p> +<blockquote class="text"><dl> +<dt>oauth_token:</dt> +<dd> + The Request Token. + +</dd> +<dt>oauth_token_secret:</dt> +<dd> + The Token Secret. + +</dd> +<dt>oauth_callback_confirmed:</dt> +<dd> + MUST be present and set to <tt>true</tt>. The Consumer + MAY use this to confirm that the Service Provider received the callback value. + +</dd> +<dt>Additional parameters:</dt> +<dd> + Any additional parameters, as defined by the Service Provider. + +</dd> +</dl></blockquote><p> + +</p> +<p> + If the request fails verification or is rejected for other reasons, + the Service Provider SHOULD respond with the appropriate response + code as defined in <a class='info' href='#http_codes'>HTTP Response Codes<span> (</span><span class='info'>HTTP Response Codes</span><span>)</span></a>. + The Service Provider MAY include some further details about why the + request was rejected in the HTTP response body as defined in + <a class='info' href='#response_parameters'>Service Provider Response Parameters<span> (</span><span class='info'>Service Provider Response Parameters</span><span>)</span></a>. + +</p> +<a name="auth_step2"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.2"></a><h3>6.2. +Obtaining User Authorization</h3> + +<p> + The Consumer cannot use the Request Token until it has been + authorized by the User. Obtaining User authorization includes + the following steps: + +</p> +<a name="user_auth_redirected"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.2.1"></a><h3>6.2.1. +Consumer Directs the User to the Service Provider</h3> + +<p> + In order for the Consumer to be able to exchange the Request Token + for an Access Token, the Consumer MUST obtain approval from the + User by directing the User to the Service Provider. The Consumer + constructs an HTTP GET request to the Service Provider's + User Authorization URL with the following parameter: + + </p> +<blockquote class="text"><dl> +<dt>oauth_token:</dt> +<dd> + OPTIONAL. The Request Token obtained in the previous step. The + Service Provider MAY declare this parameter as REQUIRED, or + accept requests to the User Authorization URL without it, in + which case it will prompt the User to enter it manually. + +</dd> +<dt>Additional parameters:</dt> +<dd> + Any additional parameters, as defined by the Service Provider. + +</dd> +</dl></blockquote><p> + +</p> +<p> + Once the request URL has been constructed the Consumer redirects + the User to the URL via the User's web browser. If the Consumer is + incapable of automatic HTTP redirection, the Consumer SHALL notify + the User how to manually go to the constructed request URL. + +</p> +<p> + Note: If a Service Provider knows a Consumer to be running on a + mobile device or set-top box, the Service Provider SHOULD ensure + that the User Authorization URL and Request Token are suitable + for manual entry. + +</p> +<a name="anchor10"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.2.2"></a><h3>6.2.2. +Service Provider Authenticates the User and Obtains Consent</h3> + +<p> + The Service Provider verifies the User's identity and asks for + consent as detailed. OAuth does not specify how the Service Provider + authenticates the User. However, it does define a set of REQUIRED + steps: + + </p> +<ul class="text"> +<li> + The Service Provider MUST first verify the User's identity + before asking for consent. It MAY prompt the User to sign + in if the User has not already done so. + +</li> +<li> + The Service Provider presents to the User information about the + Consumer requesting access (as registered by the Consumer + Developer). The information includes the duration of the + access and the Protected Resources provided. The information + MAY include other details specific to the Service Provider. + +</li> +<li> + The User MUST grant or deny permission for the Service Provider + to give the Consumer access to the Protected Resources on + behalf of the User. If the User denies the Consumer access, the + Service Provider MUST NOT allow access to the Protected + Resources. + +</li> +</ul><p> + +</p> +<p> + When displaying any identifying information about the Consumer to + the User based on the Consumer Key, the Service Provider MUST + inform the User if it is unable to assure the Consumer's true + identity. The method in which the Service Provider informs the User + and the quality of the identity assurance is beyond the scope of + this specification. + +</p> +<a name="provider_redirects"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.2.3"></a><h3>6.2.3. +Service Provider Directs the User Back to the Consumer</h3> + +<p> + After the User authenticates with the Service Provider and grants + permission for Consumer access, the Consumer MUST be notified that + the Request Token has been authorized and ready to be exchanged for + an Access Token. If the User denies access, the Consumer MAY be + notified that the Request Token has been revoked. + +</p> +<p> + To make sure that the User granting access is the same User returning + back to the Consumer to complete the process, the Service Provider MUST + generate a verification code: an unguessable value passed to the Consumer via the + User and REQUIRED to complete the process. + +</p> +<p> + If the Consumer provided a callback URL (using the <tt>oauth_callback</tt> + parameter in <a class='info' href='#obtain_request_token'>Section 6.1.1<span> (</span><span class='info'>Consumer Obtains a Request Token</span><span>)</span></a> or by other means), the Service Provider uses + it to constructs an HTTP request, and directs the User's web browser to that URL with the following + parameters added: + + </p> +<blockquote class="text"><dl> +<dt>oauth_token:</dt> +<dd> + The Request Token the User authorized or denied. + +</dd> +<dt>oauth_verifier:</dt> +<dd> + The verification code. + +</dd> +</dl></blockquote><p> + +</p> +<p> + The callback URL MAY include Consumer provided query parameters. + The Service Provider MUST retain them unmodified and append the + <tt>oauth_token</tt> parameter to the existing query. + +</p> +<p> + If the Consumer did not provide a callback URL, the Service Provider MUST display the value of the + verification code, and instruct the User to manually inform the Consumer that authorization is completed. If the Service Provider + knows a Consumer to be running on a mobile device or set-top box, the Service Provider + SHOULD ensure that the verifier value is suitable for manual entry. + +</p> +<a name="auth_step3"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.3"></a><h3>6.3. +Obtaining an Access Token</h3> + +<p> + The Consumer exchanges the Request Token for an Access Token capable + of accessing the Protected Resources. Obtaining an Access Token + includes the following steps: + +</p> +<a name="anchor11"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.3.1"></a><h3>6.3.1. +Consumer Requests an Access Token</h3> + +<p> + The Request Token and Token Secret MUST be exchanged for an Access + Token and Token Secret. + +</p> +<p> + To request an Access Token, the Consumer makes an HTTP request to + the Service Provider's Access Token URL. The Service Provider + documentation specifies the HTTP method for this request, and HTTP POST + is RECOMMENDED. The request MUST be signed per + <a class='info' href='#signing_process'>Signing Requests<span> (</span><span class='info'>Signing Requests</span><span>)</span></a>, + and contains the following parameters: + + </p> +<blockquote class="text"><dl> +<dt>oauth_consumer_key:</dt> +<dd> + The Consumer Key. + +</dd> +<dt>oauth_token:</dt> +<dd> + The Request Token obtained previously. + +</dd> +<dt>oauth_signature_method:</dt> +<dd> + The signature method the Consumer used to sign the request. + +</dd> +<dt>oauth_signature:</dt> +<dd> + The signature as defined in <a class='info' href='#signing_process'>Signing Requests<span> (</span><span class='info'>Signing Requests</span><span>)</span></a>. + +</dd> +<dt>oauth_timestamp:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_nonce:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_version:</dt> +<dd> + OPTIONAL. If present, value MUST be <tt> + 1.0 + </tt>. Service Providers + MUST assume the protocol version to be <tt>1.0</tt> if this parameter + is not present. Service Providers' response to non-<tt>1.0</tt> value + is left undefined. + +</dd> +<dt>oauth_verifier:</dt> +<dd> + The verification code received from the Service Provider in the + <a class='info' href='#provider_redirects'>Service Provider Directs the User Back to the Consumer<span> (</span><span class='info'>Service Provider Directs the User Back to the Consumer</span><span>)</span></a> step. + +</dd> +</dl></blockquote><p> + +</p> +<p> + No additional Service Provider specific parameters are allowed when + requesting an Access Token to ensure all Token related information + is present prior to seeking User approval. + +</p> +<a name="access_grant"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.6.3.2"></a><h3>6.3.2. +Service Provider Grants an Access Token</h3> + +<p> + The Service Provider MUST ensure that: + + </p> +<ul class="text"> +<li> + The request signature has been successfully verified. + +</li> +<li> + The Request Token has never been exchanged for an Access Token. + +</li> +<li> + The Request Token matches the Consumer Key. + +</li> +<li> + The verification code received from the Consumer has been successfully verified. + +</li> +</ul><p> + +</p> +<p> + If successful, the Service Provider generates an Access Token and + Token Secret and returns them in the HTTP response body as defined + in <a class='info' href='#response_parameters'>Service Provider Response Parameters<span> (</span><span class='info'>Service Provider Response Parameters</span><span>)</span></a>. + The Access Token and Token Secret are stored by the Consumer and + used when signing Protected Resources requests. The response + contains the following parameters: + + </p> +<blockquote class="text"><dl> +<dt>oauth_token:</dt> +<dd> + The Access Token. + +</dd> +<dt>oauth_token_secret:</dt> +<dd> + The Token Secret. + +</dd> +<dt>Additional parameters:</dt> +<dd> + Any additional parameters, as defined by the Service Provider. + +</dd> +</dl></blockquote><p> + +</p> +<p> + If the request fails verification or is rejected for other reasons, + the Service Provider SHOULD respond with the appropriate response + code as defined in <a class='info' href='#http_codes'>HTTP Response Codes<span> (</span><span class='info'>HTTP Response Codes</span><span>)</span></a>. + The Service Provider MAY include some further details about why the + request was rejected in the HTTP response body as defined in + <a class='info' href='#response_parameters'>Service Provider Response Parameters<span> (</span><span class='info'>Service Provider Response Parameters</span><span>)</span></a>. + +</p> +<a name="anchor12"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.7"></a><h3>7. +Accessing Protected Resources</h3> + +<p> + After successfully receiving the Access Token and Token Secret, the + Consumer is able to access the Protected Resources on behalf of the + User. The request MUST be signed per + <a class='info' href='#signing_process'>Signing Requests<span> (</span><span class='info'>Signing Requests</span><span>)</span></a>, and + contains the following parameters: + + </p> +<blockquote class="text"><dl> +<dt>oauth_consumer_key:</dt> +<dd> + The Consumer Key. + +</dd> +<dt>oauth_token:</dt> +<dd> + The Access Token. + +</dd> +<dt>oauth_signature_method:</dt> +<dd> + The signature method the Consumer used to sign the request. + +</dd> +<dt>oauth_signature:</dt> +<dd> + The signature as defined in + <a class='info' href='#signing_process'>Signing Requests<span> (</span><span class='info'>Signing Requests</span><span>)</span></a>. + +</dd> +<dt>oauth_timestamp:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_nonce:</dt> +<dd> + As defined in <a class='info' href='#nonce'>Nonce and Timestamp<span> (</span><span class='info'>Nonce and Timestamp</span><span>)</span></a>. + +</dd> +<dt>oauth_version:</dt> +<dd> + OPTIONAL. If present, value MUST be <tt>1.0</tt>. Service Providers + MUST assume the protocol version to be <tt>1.0</tt> if this parameter + is not present. Service Providers' response to non-<tt>1.0</tt> value + is left undefined. + +</dd> +<dt>Additional parameters:</dt> +<dd> + Any additional parameters, as defined by the Service Provider. + +</dd> +</dl></blockquote><p> + +</p> +<a name="nonce"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.8"></a><h3>8. +Nonce and Timestamp</h3> + +<p> + Unless otherwise specified by the Service Provider, the timestamp is + expressed in the number of seconds since January 1, 1970 00:00:00 GMT. + The timestamp value MUST be a positive integer and MUST be equal or + greater than the timestamp used in previous requests. + +</p> +<p> + The Consumer SHALL then generate a Nonce value that is unique for all + requests with that timestamp. A nonce is a random string, uniquely + generated for each request. The nonce allows the Service Provider to + verify that a request has never been made before and helps prevent + replay attacks when requests are made over a non-secure channel + (such as HTTP). + +</p> +<a name="signing_process"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9"></a><h3>9. +Signing Requests</h3> + +<p> + All Token requests and Protected Resources requests MUST be + signed by the Consumer and verified by the Service Provider. + The purpose of signing requests is to prevent unauthorized parties + from using the Consumer Key and Tokens when making Token requests or + Protected Resources requests. The signature process encodes + the Consumer Secret and Token Secret into a verifiable value which is + included with the request. + +</p> +<p> + OAuth does not mandate a particular signature method, as each + implementation can have its own unique requirements. The protocol + defines three signature methods: <tt>HMAC-SHA1</tt>, + <tt>RSA-SHA1</tt>, and + <tt>PLAINTEXT</tt>, but Service Providers + are free to implement and document their own methods. + Recommending any particular method is beyond the scope of this specification. + +</p> +<p> + The Consumer declares a signature method in the <tt>oauth_signature_method</tt> + parameter, generates a signature, and stores it in the <tt>oauth_signature</tt> + parameter. The Service Provider verifies the signature as specified in + each method. When verifying a Consumer signature, the Service Provider + SHOULD check the request nonce to ensure it has not been used in a + previous Consumer request. + +</p> +<p> + The signature process MUST NOT change the request parameter names or + values, with the exception of the <tt>oauth_signature</tt> parameter. + +</p> +<a name="anchor13"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.1"></a><h3>9.1. +Signature Base String</h3> + +<p> + The Signature Base String is a consistent reproducible concatenation + of the request elements into a single string. The string is used as an + input in hashing or signing algorithms. The <tt>HMAC-SHA1</tt> signature + method provides both a standard and an example of using the Signature + Base String with a signing algorithm to generate signatures. All + the request parameters MUST be encoded as described in + <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a> prior to + constructing the Signature Base String. + +</p> +<a name="sig_norm_param"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.1.1"></a><h3>9.1.1. +Normalize Request Parameters</h3> + +<p> + The request parameters are collected, sorted and concatenated into + a normalized string: + + </p> +<ul class="text"> +<li> + Parameters in the <a class='info' href='#auth_header_authorization'>OAuth HTTP Authorization header<span> (</span><span class='info'>Authorization Header</span><span>)</span></a> excluding the <tt>realm</tt> + parameter. + +</li> +<li> + Parameters in the HTTP POST request body (with a + <tt>content-type</tt> of + <tt>application/x-www-form-urlencoded</tt>). + +</li> +<li> + HTTP GET parameters added to the URLs in the query part (as defined by + <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> section 3). + +</li> +</ul><p> + +</p> +<p> + The <tt>oauth_signature</tt> parameter MUST be + excluded. + +</p> +<p> + The parameters are normalized into a single string as follows: + + </p> +<ol class="text"> +<li> + Parameters are sorted by name, using lexicographical byte value + ordering. If two or more parameters share the same name, they + are sorted by their value. For example: + + <div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + a=1, c=hi%20there, f=25, f=50, f=a, z=p, z=t +</pre></div> + +</li> +<li> + Parameters are concatenated in their sorted order into a single + string. For each parameter, the name is separated from the + corresponding value by an '=' character (ASCII code 61), even + if the value is empty. Each name-value pair is separated by an + '&' character (ASCII code 38). For example: + + <div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t +</pre></div> + +</li> +</ol><p> + +</p> +<a name="sig_url"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.1.2"></a><h3>9.1.2. +Construct Request URL</h3> + +<p> + The Signature Base String includes the request absolute URL, tying + the signature to a specific endpoint. The URL used in the Signature + Base String MUST include the scheme, authority, and path, and MUST + exclude the query and fragment as defined by <a class='info' href='#RFC3986'>[RFC3986]<span> (</span><span class='info'>Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .</span><span>)</span></a> + section 3. + +</p> +<p> + If the absolute request URL is not available to the Service Provider + (it is always available to the Consumer), it can be constructed by + combining the scheme being used, the HTTP <tt>Host</tt> + header, and the relative HTTP request URL. If the + <tt>Host</tt> header is not available, the Service + Provider SHOULD use the host name communicated to the Consumer in the + documentation or other means. + +</p> +<p> + The Service Provider SHOULD document the form of URL used in the + Signature Base String to avoid ambiguity due to URL normalization. + Unless specified, URL scheme and authority MUST be lowercase and + include the port number; <tt>http</tt> default + port 80 and <tt>https</tt> default port 443 MUST + be excluded. + +</p> +<p> + For example, the request: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + HTTP://Example.com:80/resource?id=123 +</pre></div><p> + + + Is included in the Signature Base String as: + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://example.com/resource +</pre></div><p> + + +</p> +<a name="anchor14"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.1.3"></a><h3>9.1.3. +Concatenate Request Elements</h3> + +<p> + The following items MUST be concatenated in order into a single + string. Each item is <a class='info' href='#encoding_parameters'>encoded<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a> + and separated by an '&' character (ASCII code 38), even if empty. + + </p> +<ol class="text"> +<li> + The HTTP request method used to send the request. Value MUST be + uppercase, for example: <tt>HEAD</tt>, <tt> + GET + </tt>, <tt>POST</tt>, etc. + +</li> +<li> + The request URL from <a class='info' href='#sig_url'>Section 9.1.2<span> (</span><span class='info'>Construct Request URL</span><span>)</span></a>. + +</li> +<li> + The normalized request parameters string from <a class='info' href='#sig_norm_param'>Section 9.1.1<span> (</span><span class='info'>Normalize Request Parameters</span><span>)</span></a>. + +</li> +</ol><p> + +</p> +<p> + See Signature Base String example in <a class='info' href='#sig_base_example'>Appendix A.5.1<span> (</span><span class='info'>Generating Signature Base String</span><span>)</span></a>. + +</p> +<a name="anchor15"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.2"></a><h3>9.2. +HMAC-SHA1</h3> + +<p> + The <tt>HMAC-SHA1</tt> signature method uses the HMAC-SHA1 signature + algorithm as defined in <a class='info' href='#RFC2104'>[RFC2104]<span> (</span><span class='info'>Krawczyk, H., Bellare, M., and R. Canetti, “HMAC: Keyed-Hashing for Message Authentication,” .</span><span>)</span></a> where the Signature + Base String is the <tt>text</tt> and the + <tt>key</tt> is the concatenated values + (each first encoded per <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>) + of the Consumer Secret and Token Secret, separated by an '&' + character (ASCII code 38) even if empty. + +</p> +<a name="anchor16"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.2.1"></a><h3>9.2.1. +Generating Signature</h3> + +<p> + <tt>oauth_signature</tt> is set + to the calculated <tt>digest</tt> octet string, first base64-encoded per + <a class='info' href='#RFC2045'>[RFC2045]<span> (</span><span class='info'>Freed, N. and N. Borenstein, “Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies,” .</span><span>)</span></a> section 6.8, then URL-encoded per + <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>. + +</p> +<a name="anchor17"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.2.2"></a><h3>9.2.2. +Verifying Signature</h3> + +<p> + The Service Provider verifies the request by generating a new request + signature octet string, and comparing it to the signature provided by the Consumer, + first URL-decoded per <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>, + then base64-decoded per <a class='info' href='#RFC2045'>[RFC2045]<span> (</span><span class='info'>Freed, N. and N. Borenstein, “Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies,” .</span><span>)</span></a> section 6.8. + The signature is generated using the request parameters as provided + by the Consumer, and the Consumer Secret and Token Secret as stored + by the Service Provider. + +</p> +<a name="anchor18"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.3"></a><h3>9.3. +RSA-SHA1</h3> + +<p> + The <tt>RSA-SHA1</tt> signature method uses the + RSASSA-PKCS1-v1_5 signature algorithm as defined in + <a class='info' href='#RFC3447'>[RFC3447]<span> (</span><span class='info'>Jonsson, J. and B. Kaliski, “Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography; Specifications Version 2.1,” .</span><span>)</span></a> section 8.2 (more simply known as PKCS#1), + using SHA-1 as the hash function for EMSA-PKCS1-v1_5. It is assumed + that the Consumer has provided its RSA public key in a verified way + to the Service Provider, in a manner which is beyond the scope of + this specification. + +</p> +<a name="anchor19"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.3.1"></a><h3>9.3.1. +Generating Signature</h3> + +<p> + The Signature Base String is signed using the Consumer's RSA private + key per <a class='info' href='#RFC3447'>[RFC3447]<span> (</span><span class='info'>Jonsson, J. and B. Kaliski, “Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography; Specifications Version 2.1,” .</span><span>)</span></a> section 8.2.1, where <tt>K</tt> is the + Consumer's RSA private key, <tt>M</tt> the Signature Base String, and <tt>S</tt> is + the result signature octet string: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + S = RSASSA-PKCS1-V1_5-SIGN (K, M) +</pre></div><p> + + +</p> +<p> + <tt>oauth_signature</tt> is set to <tt>S</tt>, first base64-encoded per + <a class='info' href='#RFC2045'>[RFC2045]<span> (</span><span class='info'>Freed, N. and N. Borenstein, “Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies,” .</span><span>)</span></a> section 6.8, then URL-encoded per + <a class='info' href='#encoding_parameters'>Parameter Encoding<span> (</span><span class='info'>Parameter Encoding</span><span>)</span></a>. + +</p> +<a name="anchor20"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.3.2"></a><h3>9.3.2. +Verifying Signature</h3> + +<p> + The Service Provider verifies the signature per <a class='info' href='#RFC3447'>[RFC3447]<span> (</span><span class='info'>Jonsson, J. and B. Kaliski, “Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography; Specifications Version 2.1,” .</span><span>)</span></a> + section 8.2.2, where <tt> + (n, e) + </tt> is the Consumer's RSA public key, <tt>M</tt> + is the Signature Base String, and <tt>S</tt> is the octet string + representation of the <tt>oauth_signature</tt> value: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S) +</pre></div><p> + + +</p> +<a name="anchor21"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.4"></a><h3>9.4. +PLAINTEXT</h3> + +<p> + The <tt> + PLAINTEXT + </tt> method does not provide any security protection and + SHOULD only be used over a secure channel such as HTTPS. It does not + use the Signature Base String. + +</p> +<a name="anchor22"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.4.1"></a><h3>9.4.1. +Generating Signature</h3> + +<p> + <tt>oauth_signature</tt> is set to the concatenated encoded values of the + Consumer Secret and Token Secret, separated by a '&' character (ASCII + code 38), even if either secret is empty. The result MUST be encoded again. + +</p> +<p> + These examples show the value of <tt>oauth_signature</tt> + for Consumer Secret <tt>djr9rjt0jd78jf88</tt> and + 3 different Token Secrets: + + </p> +<blockquote class="text"><dl> +<dt>jjd999tj88uiths3:</dt> +<dd> + <tt>oauth_signature</tt>=<tt>djr9rjt0jd78jf88%26jjd999tj88uiths3</tt> + +</dd> +<dt>jjd99$tj88uiths3:</dt> +<dd> + <tt>oauth_signature</tt>=<tt>djr9rjt0jd78jf88%26jjd99%2524tj88uiths3</tt> + +</dd> +<dt>Empty:</dt> +<dd> + <tt>oauth_signature</tt>=<tt>djr9rjt0jd78jf88%26</tt> + +</dd> +</dl></blockquote><p> + +</p> +<a name="anchor23"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.9.4.2"></a><h3>9.4.2. +Verifying Signature</h3> + +<p> + The Service Provider verifies the request by breaking the signature + value into the Consumer Secret and Token Secret, and ensures they + match the secrets stored locally. + +</p> +<a name="http_codes"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.10"></a><h3>10. +HTTP Response Codes</h3> + +<p> + This section applies only to the Request Token and Access Token + requests. In general, the Service Provider SHOULD use the + response codes defined in <a class='info' href='#RFC2616'>[RFC2616]<span> (</span><span class='info'>Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, “Hypertext Transfer Protocol -- HTTP/1.1,” .</span><span>)</span></a> Section 10. When + the Service Provider rejects a Consumer request, it SHOULD respond with + HTTP 400 Bad Request or HTTP 401 Unauthorized. + + </p> +<ul class="text"> +<li> + HTTP 400 Bad Request + +<ul class="text"> +<li> + Unsupported parameter + +</li> +<li> + Unsupported signature method + +</li> +<li> + Missing required parameter + +</li> +<li> + Duplicated OAuth Protocol Parameter + +</li> +</ul> + +</li> +<li> + HTTP 401 Unauthorized + +<ul class="text"> +<li> + Invalid Consumer Key + +</li> +<li> + Invalid / expired Token + +</li> +<li> + Invalid signature + +</li> +<li> + Invalid / used nonce + +</li> +</ul> + +</li> +</ul><p> + +</p> +<a name="anchor24"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11"></a><h3>11. +Security Considerations</h3> + +<a name="anchor25"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.1"></a><h3>11.1. +Credentials and Token Exchange</h3> + +<p> + The OAuth specification does not describe any mechanism for protecting + Tokens and secrets from eavesdroppers when they are transmitted from + the Service Provider to the Consumer in <a class='info' href='#request_grant'>Section 6.1.2<span> (</span><span class='info'>Service Provider Issues an Unauthorized Request Token</span><span>)</span></a> + and <a class='info' href='#access_grant'>Section 6.3.2<span> (</span><span class='info'>Service Provider Grants an Access Token</span><span>)</span></a>. Service Providers should ensure + that these transmissions are protected using transport-layer mechanisms + such as TLS or SSL. + +</p> +<a name="anchor26"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.2"></a><h3>11.2. +PLAINTEXT Signature Method</h3> + +<p> + When used with <tt>PLAINTEXT</tt> signatures, the + OAuth protocol makes no attempts to protect User credentials from + eavesdroppers or man-in-the-middle attacks. + The <tt>PLAINTEXT</tt> signature algorithm is only + intended to be used in conjunction with a transport-layer security + mechanism such as TLS or SSL which does provide such protection. + If transport-layer protection is unavailable, the + <tt>PLAINTEXT</tt> signature method should not be + used. + +</p> +<a name="anchor27"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.3"></a><h3>11.3. +Confidentiality of Requests</h3> + +<p> + While OAuth provides a mechanism for verifying the integrity of + requests, it provides no guarantee of request confidentiality. + Unless further precautions are taken, eavesdroppers will have full + access to request content. Service Providers should carefully + consider the kinds of data likely to be sent as part of such requests, + and should employ transport-layer security mechanisms to protect + sensitive resources. + +</p> +<a name="anchor28"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.4"></a><h3>11.4. +Spoofing by Counterfeit Servers</h3> + +<p> + OAuth makes no attempt to verify the authenticity of the Service + Provider. A hostile party could take advantage of this by intercepting + the Consumer's requests and returning misleading or otherwise incorrect + responses. Service providers should consider such attacks when + developing services based on OAuth, and should require transport-layer + security for any requests where the authenticity of the Service + Provider or of request responses is an issue. + +</p> +<a name="anchor29"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.5"></a><h3>11.5. +Proxying and Caching of Authenticated Content</h3> + +<p> + The <a class='info' href='#auth_header'>HTTP Authorization scheme<span> (</span><span class='info'>OAuth HTTP Authorization Scheme</span><span>)</span></a> is + optional. However, <a class='info' href='#RFC2616'>[RFC2616]<span> (</span><span class='info'>Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, “Hypertext Transfer Protocol -- HTTP/1.1,” .</span><span>)</span></a> relies on the + <tt>Authorization</tt> and + <tt>WWW-Authenticate</tt> headers to distinguish + authenticated content so that it can be protected. Proxies and + caches, in particular, may fail to adequately protect requests not + using these headers. + +</p> +<p> + For example, private authenticated content may be stored in (and thus + retrievable from) publicly-accessible caches. Service Providers not + using the <a class='info' href='#auth_header'>HTTP Authorization scheme<span> (</span><span class='info'>OAuth HTTP Authorization Scheme</span><span>)</span></a> + should take care to use other mechanisms, such as the + <tt>Cache-Control</tt> header, to ensure that + authenticated content is protected. + +</p> +<a name="anchor30"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.6"></a><h3>11.6. +Plaintext Storage of Credentials</h3> + +<p> + The Consumer Secret and Token Secret function the same way passwords + do in traditional authentication systems. In order to compute the + signatures used in the non-<tt>PLAINTEXT</tt> + methods, the Service Provider must have access to these secrets in + plaintext form. This is in contrast, for example, to modern operating + systems, which store only a one-way hash of user credentials. + +</p> +<p> + If an attacker were to gain access to these secrets - or worse, to + the Service Provider's database of all such secrets - he or she would + be able to perform any action on behalf of any User. Accordingly, it + is critical that Service Providers protect these secrets from + unauthorized access. + +</p> +<a name="anchor31"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.7"></a><h3>11.7. +Secrecy of the Consumer Secret</h3> + +<p> + In many applications, the Consumer application will be under the + control of potentially untrusted parties. For example, if the + Consumer is a freely available desktop application, an attacker may + be able to download a copy for analysis. In such cases, attackers + will be able to recover the Consumer Secret used to authenticate the + Consumer to the Service Provider. + +</p> +<p> + Accordingly, Service Providers should not use the Consumer Secret + alone to verify the identity of the Consumer. Where possible, other + factors such as IP address should be used as well. + +</p> +<a name="anchor32"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.8"></a><h3>11.8. +Phishing Attacks</h3> + +<p> + Wide deployment of OAuth and similar protocols may cause + Users to become inured to the practice of being redirected to + websites where they are asked to enter their passwords. If Users are + not careful to verify the authenticity of these websites before + entering their credentials, it will be possible for attackers to + exploit this practice to steal Users' passwords. + +</p> +<p> + Service Providers should attempt to educate Users about the risks + phishing attacks pose, and should provide mechanisms that make it + easy for Users to confirm the authenticity of their sites. + +</p> +<a name="anchor33"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.9"></a><h3>11.9. +Scoping of Access Requests</h3> + +<p> + By itself, OAuth does not provide any method for scoping the access + rights granted to a Consumer. A Consumer either has access to + Protected Resources or it doesn't. Many applications will, however, + require greater granularity of access rights. For example, Service + Providers may wish to make it possible to grant access to some + Protected Resources but not others, or to grant only limited access + (such as read-only access) to those Protected Resources. + +</p> +<p> + When implementing OAuth, Service Providers should consider the types + of access Users may wish to grant Consumers, and should provide + mechanisms to do so. Service Providers should also take care to + ensure that Users understand the access they are granting, as well as + any risks that may be involved. + +</p> +<a name="anchor34"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.10"></a><h3>11.10. +Entropy of Secrets</h3> + +<p> + Unless a transport-layer security protocol is used, eavesdroppers will + have full access to OAuth requests and signatures, and will thus be + able to mount offline brute-force attacks to recover the Consumer's + credentials used. Service Providers should be careful to assign Token + Secrets and Consumer Secrets which are long enough - and random enough + - to resist such attacks for at least the length of time that the + secrets are valid. + +</p> +<p> + For example, if Token Secrets are valid for two weeks, Service + Providers should ensure that it is not possible to mount a brute force + attack that recovers the Token Secret in less than two weeks. Of + course, Service Providers are urged to err on the side of caution, + and use the longest secrets reasonable. + +</p> +<p> + It is equally important that the pseudo-random number generator (PRNG) + used to generate these secrets be of sufficiently high quality. Many + PRNG implementations generate number sequences that may appear to be + random, but which nevertheless exhibit patterns or other weaknesses + which make cryptanalysis or brute force attacks easier. Implementors + should be careful to use cryptographically secure PRNGs to avoid these + problems. + +</p> +<a name="anchor35"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.11"></a><h3>11.11. +Denial of Service / Resource Exhaustion Attacks</h3> + +<p> + The OAuth protocol has a number of features which may make resource + exhaustion attacks against Service Providers possible. For example, + if a Service Provider includes a nontrivial amount of entropy in Token + Secrets as recommended above, then an attacker may be able to exhaust + the Service Provider's entropy pool very quickly by repeatedly + obtaining Request Tokens from the Service Provider. + +</p> +<p> + Similarly, OAuth requires Service Providers to track used nonces. If + an attacker is able to use many nonces quickly, the resources required + to track them may exhaust available capacity. And again, OAuth can + require Service Providers to perform potentially expensive computations + in order to verify the signature on incoming requests. An attacker may + exploit this to perform a denial of service attack by sending a large + number of invalid requests to the Service Provider. + +</p> +<p> + Resource Exhaustion attacks are by no means specific to OAuth. However, + OAuth implementors should be careful to consider the additional + avenues of attack that OAuth exposes, and design their implementations + accordingly. For example, entropy starvation typically results in + either a complete denial of service while the system waits for new + entropy or else in weak (easily guessable) secrets. When implementing + OAuth, Service Providers should consider which of these presents a + more serious risk for their application and design accordingly. + +</p> +<a name="anchor36"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.12"></a><h3>11.12. +Cryptographic Attacks</h3> + +<p> + SHA-1, the hash algorithm used in <tt>HMAC-SHA1</tt> + signatures, has been <a class='info' href='#SHA1'>shown<span> (</span><span class='info'>De Canniere, C. and C. Rechberger, “Finding SHA-1 Characteristics: General Results and Applications,” .</span><span>)</span></a> [SHA1] to have a number + of cryptographic weaknesses that significantly reduce its resistance to + collision attacks. Practically speaking, these weaknesses are difficult + to exploit, and by themselves do not pose a significant risk to users + of OAuth. They may, however, make more efficient attacks possible, and + NIST has <a class='info' href='#NIST'>announced<span> (</span><span class='info'>National Institute of Standards and Technolog, NIST., “NIST Brief Comments on Recent Cryptanalytic Attacks on Secure Hashing Functions and the Continued Security Provided by SHA-1,” .</span><span>)</span></a> [NIST] that it will phase out + use of SHA-1 by 2010. Service Providers should take this into account + when considering whether SHA-1 provides an adequate level of security + for their applications. + +</p> +<a name="anchor37"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.13"></a><h3>11.13. +Signature Base String Compatibility</h3> + +<p> + The Signature Base String has been designed to support the signature + methods defined in this specification. When designing additional + signature methods, the Signature Base String should be evaluated to + ensure compatibility with the algorithms used. + +</p> +<p> + The Signature Base String cannot guarantee the order in which parameters + are sent. If parameter ordering is important and affects the result of a + request, the Signature Base String will not protect against request + manipulation. + +</p> +<a name="anchor38"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.14"></a><h3>11.14. +Cross-Site Request Forgery (CSRF)</h3> + +<p> + Cross-Site Request Forgery (CSRF) is a web-based attack whereby HTTP requests + are transmitted from a user that the website trusts or has authenticated. + CSRF attacks on OAuth approvals can allow an attacker to obtain authorization to + OAuth Protected Resources without the consent of the User. Service Providers + SHOULD strongly consider best practices in CSRF prevention at all OAuth endpoints. + +</p> +<p> + CSRF attacks on OAuth callback URLs hosted by Consumers are also possible. + Consumers should prevent CSRF attacks on OAuth callback URLs by verifying that + the User at the Consumer site intended to complete the OAuth negotiation with the + Service Provider. + +</p> +<a name="anchor39"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.15"></a><h3>11.15. +User Interface Redress</h3> + +<p> + Service Providers should protect the authorization process against UI Redress attacks + (also known as "clickjacking"). As of the time of this writing, no complete defenses + against UI redress are available. Service Providers can mitigate the risk of UI + redress attacks through the following techniques: + + </p> +<ul class="text"> +<li>Javascript frame busting. +</li> +<li>Javascript frame busting, and requiring that browsers have javascript enabled on the authorization page. +</li> +<li>Browser-specific anti-framing techniques. +</li> +<li>Requiring password reentry before issuing OAuth tokens. +</li> +</ul><p> + +</p> +<a name="anchor40"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.11.16"></a><h3>11.16. +Automatic Processing of Repeat Authorizations</h3> + +<p> + Service Providers may wish to automatically process authorization requests + (<a class='info' href='#auth_step2'>Section 6.2<span> (</span><span class='info'>Obtaining User Authorization</span><span>)</span></a>) from Consumers which have been previously + authorized by the user. When the User is redirected to the Service Provider + to grant access, the Service Provider detects that the User has already granted + access to that particular Consumer. Instead of prompting the User for approval, + the Service Provider automatically redirects the User back to the Provider. + +</p> +<p> + If the Consumer Secret is compromised, automatic processing creates additional + security risks. An attacker can use the stolen Consumer Key and Secret to redirect + the User to the Service Provider with an authorization request. The Service Provider + will then grant access to the User's data without the User's explicit approval, or + even awareness of an attack. If no automatic approval is implemented, an attacker + must use social engineering to convince the User to approve access. + +</p> +<p> + Service Providers can mitigate the risks associated with automatic processing by + limiting the scope of Access Tokens obtained through automated approvals. Access + Tokens obtained through explicit User consent can remain unaffected. Consumers can + mitigate the risks associated with automatic processing by protecting their Consumer + Secret. + +</p> +<a name="anchor41"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A"></a><h3>Appendix A. +Appendix A - Protocol Example</h3> + +<p> + In this example, the Service Provider photos.example.net is a photo + sharing website, and the Consumer printer.example.com is a photo + printing website. Jane, the User, would like printer.example.com to + print the private photo <tt> + vacation.jpg + </tt> stored at photos.example.net. + +</p> +<p> + When Jane signs-into photos.example.net using her username and + password, she can access the photo by going to the URL + <tt>http://photos.example.net/photo?file=vacation.jpg</tt>. Other Users + cannot access that photo, and Jane does not want to share her + username and password with printer.example.com. + +</p> +<p> + The requests in this example use the URL query method when sending + parameters. This is done to simplify the example and should not be + taken as an endorsement of one method over the others. + +</p> +<a name="anchor42"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.1"></a><h3>Appendix A.1. +Documentation and Registration</h3> + +<p> + The Service Provider documentation explains how to register for a + Consumer Key and Consumer Secret, and declares the following URLs: + + </p> +<blockquote class="text"><dl> +<dt>Request Token URL:</dt> +<dd> + https://photos.example.net/request_token, using HTTP POST + +</dd> +<dt>User Authorization URL:</dt> +<dd> + http://photos.example.net/authorize, using HTTP GET + +</dd> +<dt>Access Token URL:</dt> +<dd> + https://photos.example.net/access_token, using HTTP POST + +</dd> +<dt>Photo (Protected Resource) URL:</dt> +<dd> + http://photos.example.net/photo with required parameter + <tt>file</tt> and optional parameter <tt>size</tt> + +</dd> +</dl></blockquote><p> + +</p> +<p> + The Service Provider declares support for the <tt> + HMAC-SHA1 + </tt> signature + method for all requests, and <tt>PLAINTEXT</tt> only for secure (HTTPS) + requests. + +</p> +<p> + The Consumer printer.example.com already established a Consumer Key + and Consumer Secret with photos.example.net and advertizes its + printing services for photos stored on photos.example.net. The + Consumer registration is: + + </p> +<blockquote class="text"><dl> +<dt>Consumer Key:</dt> +<dd> + <tt> + dpf43f3p2l4k3l03 + </tt> + +</dd> +<dt>Consumer Secret:</dt> +<dd> + <tt>kd94hf93k423kf44</tt> + +</dd> +</dl></blockquote><p> + +</p> +<a name="anchor43"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.2"></a><h3>Appendix A.2. +Obtaining a Request Token</h3> + +<p> + After Jane informs printer.example.com that she would like to print + her vacation photo stored at photos.example.net, the printer website + tries to access the photo and receives HTTP 401 Unauthorized + indicating it is private. The Service Provider includes the following + header with the response: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + WWW-Authenticate: OAuth realm="http://photos.example.net/" +</pre></div><p> + + +</p> +<p> + The Consumer sends the following HTTP POST request to the Service + Provider: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + https://photos.example.net/request_token?oauth_consumer_key=dpf43f3p2l4k3l03&oauth_signature_method=PLAINTEXT&oauth_signature=kd94hf93k423kf44%26&oauth_timestamp=1191242090&oauth_nonce=hsu94j3884jdopsl&oauth_version=1.0&oauth_callback=http%3A%2F%2Fprinter.example.com%2Frequest_token_ready +</pre></div><p> + + +</p> +<p> + The Service Provider checks the signature and replies with an + unauthorized Request Token in the body of the HTTP response: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03 +</pre></div><p> + + +</p> +<a name="anchor44"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.3"></a><h3>Appendix A.3. +Requesting User Authorization</h3> + +<p> + The Consumer redirects Jane's browser to the Service Provider + User Authorization URL to obtain Jane's approval for accessing + her private photos. + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola +</pre></div><p> + + +</p> +<p> + The Service Provider asks Jane to sign-in using her username and + password and, if successful, asks her if she approves granting + printer.example.com access to her private photos. If Jane approves + the request, the Service Provider generates a verification code and + redirects her back to the Consumer's callback URL: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://printer.example.com/request_token_ready?oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884 +</pre></div><p> + + +</p> +<a name="anchor45"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.4"></a><h3>Appendix A.4. +Obtaining an Access Token</h3> + +<p> + Now that the Consumer knows Jane approved the Request Token, it + asks the Service Provider to exchange it for an Access Token: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + https://photos.example.net/access_token?oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=hh5s93j4hdidpola&oauth_signature_method=PLAINTEXT&oauth_signature=kd94hf93k423kf44%26hdhd0244k9j7ao03&oauth_timestamp=1191242092&oauth_nonce=dji430splmx33448&oauth_version=1.0&oauth_verifier=hfdp7dh39dks9884 +</pre></div><p> + + +</p> +<p> + The Service Provider checks the signature and the verification code and replies with an + Access Token in the body of the HTTP response: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00 +</pre></div><p> + + +</p> +<a name="anchor46"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.5"></a><h3>Appendix A.5. +Accessing Protected Resources</h3> + +<p> + The Consumer is now ready to request the private photo. Since the + photo URL is not secure (HTTP), it must use <tt>HMAC-SHA1</tt>. + +</p> +<a name="sig_base_example"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.5.1"></a><h3>Appendix A.5.1. +Generating Signature Base String</h3> + +<p> + To generate the signature, it first needs to generate the Signature + Base String. The request contains the following parameters + (<tt>oauth_signature</tt> excluded) which are ordered and concatenated into + a normalized string: + + </p> +<blockquote class="text"><dl> +<dt>oauth_consumer_key:</dt> +<dd> + <tt>dpf43f3p2l4k3l03</tt> + +</dd> +<dt>oauth_token:</dt> +<dd> + <tt>nnch734d00sl2jdk</tt> + +</dd> +<dt>oauth_signature_method:</dt> +<dd> + <tt>HMAC-SHA1</tt> + +</dd> +<dt>oauth_timestamp:</dt> +<dd> + <tt>1191242096</tt> + +</dd> +<dt>oauth_nonce:</dt> +<dd> + <tt>kllo9940pd9333jh</tt> + +</dd> +<dt>oauth_version:</dt> +<dd> + <tt>1.0</tt> + +</dd> +<dt>file:</dt> +<dd> + <tt>vacation.jpg</tt> + +</dd> +<dt>size:</dt> +<dd> + <tt>original</tt> + +</dd> +</dl></blockquote><p> + +</p> +<p> + The following inputs are used to generate the Signature Base String: + + </p> +<ol class="text"> +<li> + <tt>GET</tt> + +</li> +<li> + <tt>http://photos.example.net/photos</tt> + +</li> +<li> + <tt>file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original</tt> + +</li> +</ol><p> + +</p> +<p> + The Signature Base String is: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal +</pre></div><p> + + +</p> +<a name="anchor47"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.5.2"></a><h3>Appendix A.5.2. +Calculating Signature Value</h3> + +<p> + HMAC-SHA1 produces the following <tt>digest</tt> value as a base64-encoded + string (using the Signature Base String as <tt>text</tt> and + <tt> + kd94hf93k423kf44&pfkkdhi9sl3r4s00 + </tt> as <tt>key</tt>): + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + tR3+Ty81lMeYAr/Fid0kMTYa/WM= +</pre></div><p> + + +</p> +<a name="anchor48"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<a name="rfc.section.A.5.3"></a><h3>Appendix A.5.3. +Requesting Protected Resource</h3> + +<p> + All together, the Consumer request for the photo is: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://photos.example.net/photos?file=vacation.jpg&size=original + + Authorization: OAuth realm="http://photos.example.net/", + oauth_consumer_key="dpf43f3p2l4k3l03", + oauth_token="nnch734d00sl2jdk", + oauth_signature_method="HMAC-SHA1", + oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", + oauth_timestamp="1191242096", + oauth_nonce="kllo9940pd9333jh", + oauth_version="1.0" +</pre></div><p> + + +</p> +<p> + And if using query parameters: + + </p> +<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre> + http://photos.example.net/photos?file=vacation.jpg&size=original&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_signature_method=HMAC-SHA1&oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_version=1.0 +</pre></div><p> + + +</p> +<p> + photos.example.net checks the signature and responds with the + requested photo. + +</p> +<a name="rfc.references1"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<h3>12. References</h3> +<table width="99%" border="0"> +<tr><td class="author-text" valign="top"><a name="NIST">[NIST]</a></td> +<td class="author-text">National Institute of Standards and Technolog, NIST., “<a href="http://csrc.nist.gov/hash_standards_comments.pdf">NIST Brief Comments on Recent Cryptanalytic Attacks on Secure Hashing Functions and the Continued Security Provided by SHA-1</a>.”</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2045">[RFC2045]</a></td> +<td class="author-text">Freed, N. and N. Borenstein, “<a href="http://tools.ietf.org/html/rfc2045">Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</a>,” RFC 2045.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2104">[RFC2104]</a></td> +<td class="author-text">Krawczyk, H., Bellare, M., and R. Canetti, “<a href="http://tools.ietf.org/html/rfc2104">HMAC: Keyed-Hashing for Message Authentication</a>,” RFC 2104.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2119">[RFC2119]</a></td> +<td class="author-text">Bradner, B., “<a href="http://tools.ietf.org/html/rfc2119">Key words for use in RFCs to Indicate Requirement Levels</a>,” RFC 2119.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2606">[RFC2606]</a></td> +<td class="author-text">Eastlake, D. and A. Panitz, “<a href="http://tools.ietf.org/html/rfc2606">Reserved Top Level DNS Names</a>,” RFC 2606.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2616">[RFC2616]</a></td> +<td class="author-text">Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, “<a href="http://tools.ietf.org/html/rfc2616">Hypertext Transfer Protocol -- HTTP/1.1</a>,” RFC 2616.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC2617">[RFC2617]</a></td> +<td class="author-text">Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, “<a href="http://tools.ietf.org/html/rfc2617">HTTP Authentication: Basic and Digest Access Authentication</a>,” RFC 2617.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC3447">[RFC3447]</a></td> +<td class="author-text">Jonsson, J. and B. Kaliski, “<a href="http://tools.ietf.org/html/rfc3447">Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography; Specifications Version 2.1</a>,” RFC 3447.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC3629">[RFC3629]</a></td> +<td class="author-text">Yergeau, F., “<a href="http://tools.ietf.org/html/rfc3629">UTF-8, a transformation format of Unicode and ISO 10646</a>,” RFC 3629.</td></tr> +<tr><td class="author-text" valign="top"><a name="RFC3986">[RFC3986]</a></td> +<td class="author-text">Berners-Lee, T., “<a href="http://tools.ietf.org/html/rfc3986">Uniform Resource Identifiers (URI): Generic Syntax</a>,” RFC 3986.</td></tr> +<tr><td class="author-text" valign="top"><a name="SHA1">[SHA1]</a></td> +<td class="author-text">De Canniere, C. and C. Rechberger, “<a href="http://dx.doi.org/10.1007/11935230_1">Finding SHA-1 Characteristics: General Results and Applications</a>.”</td></tr> +</table> + +<a name="rfc.authors"></a><br /><hr /> +<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc"> TOC </a></td></tr></table> +<h3>Author's Address</h3> +<table width="99%" border="0" cellpadding="0" cellspacing="0"> +<tr><td class="author-text"> </td> +<td class="author-text">OAuth Core Workgroup</td></tr> +<tr><td class="author" align="right">Email: </td> +<td class="author-text"><a href="mailto:spec@oauth.net">spec@oauth.net</a></td></tr> +</table> +<script type="text/javascript"> +var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); +document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); +</script> + +<script type="text/javascript"> +try { +var pageTracker = _gat._getTracker("UA-2649949-1"); +pageTracker._trackPageview(); +} catch(err) {}</script> +</body></html> + diff --git a/lib/Moq.dll b/lib/Moq.dll Binary files differnew file mode 100644 index 0000000..7cab162 --- /dev/null +++ b/lib/Moq.dll diff --git a/lib/Moq.pdb b/lib/Moq.pdb Binary files differnew file mode 100644 index 0000000..3964e06 --- /dev/null +++ b/lib/Moq.pdb diff --git a/lib/Moq.xml b/lib/Moq.xml new file mode 100644 index 0000000..54caa92 --- /dev/null +++ b/lib/Moq.xml @@ -0,0 +1,3467 @@ +<?xml version="1.0"?> +<doc> + <assembly> + <name>Moq</name> + </assembly> + <members> + <member name="T:Moq.EmptyDefaultValueProvider"> + <summary> + A <see cref="T:Moq.IDefaultValueProvider"/> that returns an empty default value + for invocations that do not have setups or return values, with loose mocks. + This is the default behavior for a mock. + </summary> + </member> + <member name="T:Moq.IDefaultValueProvider"> + <summary> + Interface to be implemented by classes that determine the + default value of non-expected invocations. + </summary> + </member> + <member name="M:Moq.IDefaultValueProvider.ProvideDefault(System.Reflection.MethodInfo,System.Object[])"> + <summary> + Provides a value for the given member and arguments. + </summary> + <param name="member">The member to provide a default + value for.</param> + <param name="arguments">Optional arguments passed in + to the call that requires a default value.</param> + </member> + <member name="T:Moq.Language.Flow.IReturnsResult`1"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.ICallback"> + <summary> + Defines the <c>Callback</c> verb and overloads. + </summary> + </member> + <member name="T:Moq.IHideObjectMembers"> + <summary> + Helper interface used to hide the base <see cref="T:System.Object"/> + members from the fluent API to make it much cleaner + in Visual Studio intellisense. + </summary> + </member> + <member name="M:Moq.IHideObjectMembers.GetType"> + <summary/> + </member> + <member name="M:Moq.IHideObjectMembers.GetHashCode"> + <summary/> + </member> + <member name="M:Moq.IHideObjectMembers.ToString"> + <summary/> + </member> + <member name="M:Moq.IHideObjectMembers.Equals(System.Object)"> + <summary/> + </member> + <member name="M:Moq.Language.ICallback.Callback(System.Action)"> + <summary> + Specifies a callback to invoke when the method is called. + </summary> + <param name="action">Callback method to invoke.</param> + <example> + The following example specifies a callback to set a boolean + value that can be used later: + <code> + bool called = false; + mock.Setup(x => x.Execute()) + .Callback(() => called = true); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback.Callback``1(System.Action{``0})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T">Argument type of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation argument value. + <para> + Notice how the specific string argument is retrieved by simply declaring + it as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute(It.IsAny<string>())) + .Callback((string command) => Console.WriteLine(command)); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback.Callback``2(System.Action{``0,``1})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>())) + .Callback((string arg1, string arg2) => Console.WriteLine(arg1 + arg2)); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback.Callback``3(System.Action{``0,``1,``2})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>())) + .Callback((string arg1, string arg2, int arg3) => Console.WriteLine(arg1 + arg2 + arg3)); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback.Callback``4(System.Action{``0,``1,``2,``3})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <typeparam name="T4">Type of the fourth argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>(), + It.IsAny<bool>())) + .Callback((string arg1, string arg2, int arg3, bool arg4) => Console.WriteLine(arg1 + arg2 + arg3 + arg4)); + </code> + </example> + </member> + <member name="T:Moq.Language.IOccurrence"> + <summary> + Defines occurrence members to constraint setups. + </summary> + </member> + <member name="M:Moq.Language.IOccurrence.AtMostOnce"> + <summary> + The expected invocation can happen at most once. + </summary> + <example> + <code> + var mock = new Mock<ICommand>(); + mock.Setup(foo => foo.Execute("ping")) + .AtMostOnce(); + </code> + </example> + </member> + <member name="M:Moq.Language.IOccurrence.AtMost(System.Int32)"> + <summary> + The expected invocation can happen at most specified number of times. + </summary> + <param name="callCount">The number of times to accept calls.</param> + <example> + <code> + var mock = new Mock<ICommand>(); + mock.Setup(foo => foo.Execute("ping")) + .AtMost( 5 ); + </code> + </example> + </member> + <member name="T:Moq.Language.IRaise`1"> + <summary> + Defines the <c>Raises</c> verb. + </summary> + </member> + <member name="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"> + <summary> + Specifies the event that will be raised + when the setup is met. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="args">The event arguments to pass for the raised event.</param> + <example> + The following example shows how to raise an event when + the setup is met: + <code> + var mock = new Mock<IContainer>(); + + mock.Setup(add => add.Add(It.IsAny<string>(), It.IsAny<object>())) + .Raises(add => add.Added += null, EventArgs.Empty); + </code> + </example> + </member> + <member name="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.Func{System.EventArgs})"> + <summary> + Specifies the event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <seealso cref="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise`1.Raises``1(System.Action{`0},System.Func{``0,System.EventArgs})"> + <summary> + Specifies the event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise`1.Raises``2(System.Action{`0},System.Func{``0,``1,System.EventArgs})"> + <summary> + Specifies the event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise`1.Raises``3(System.Action{`0},System.Func{``0,``1,``2,System.EventArgs})"> + <summary> + Specifies the event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <typeparam name="T3">Type of the third argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise`1.Raises``4(System.Action{`0},System.Func{``0,``1,``2,``3,System.EventArgs})"> + <summary> + Specifies the event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <typeparam name="T3">Type of the third argument received by the expected invocation.</typeparam> + <typeparam name="T4">Type of the fourth argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise`1.Raises(System.Action{`0},System.Object[])"> + <summary> + Specifies the custom event that will be raised + when the setup is matched. + </summary> + <param name="eventExpression">An expression that represents an event attach or detach action.</param> + <param name="args">The arguments to pass to the custom delegate (non EventHandler-compatible).</param> + </member> + <member name="T:Moq.Language.IRaise"> + <summary> + Defines the <c>Raises</c> verb. + </summary> + </member> + <member name="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"> + <summary> + Specifies the mocked event that will be raised + when the setup is met. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="args">The event args to pass when raising the event.</param> + <example> + The following example shows how to raise an event when + the setup is met: + <code> + var mock = new Mock<IContainer>(); + // create handler to associate with the event to raise + var handler = mock.CreateEventHandler(); + // associate the handler with the event to raise + mock.Object.Added += handler; + // setup the invocation and the handler to raise + mock.Setup(add => add.Add(It.IsAny<string>(), It.IsAny<object>())) + .Raises(handler, EventArgs.Empty); + </code> + </example> + </member> + <member name="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.Func{System.EventArgs})"> + <summary> + Specifies the mocked event that will be raised + when the setup is matched. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <seealso cref="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise.Raises``1(Moq.MockedEvent,System.Func{``0,System.EventArgs})"> + <summary> + Specifies the mocked event that will be raised + when the setup is matched. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T">Type of the argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise.Raises``2(Moq.MockedEvent,System.Func{``0,``1,System.EventArgs})"> + <summary> + Specifies the mocked event that will be raised + when the setup is matched. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise.Raises``3(Moq.MockedEvent,System.Func{``0,``1,``2,System.EventArgs})"> + <summary> + Specifies the mocked event that will be raised + when the setup is matched. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <typeparam name="T3">Type of the third argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"/> + </member> + <member name="M:Moq.Language.IRaise.Raises``4(Moq.MockedEvent,System.Func{``0,``1,``2,``3,System.EventArgs})"> + <summary> + Specifies the mocked event that will be raised + when the setup is matched. + </summary> + <param name="eventHandler">The mocked event, retrieved from + <see cref="M:Moq.Mock.CreateEventHandler"/> or <see cref="M:Moq.Mock.CreateEventHandler``1"/>. + </param> + <param name="func">A function that will build the <see cref="T:System.EventArgs"/> + to pass when raising the event.</param> + <typeparam name="T1">Type of the first argument received by the expected invocation.</typeparam> + <typeparam name="T2">Type of the second argument received by the expected invocation.</typeparam> + <typeparam name="T3">Type of the third argument received by the expected invocation.</typeparam> + <typeparam name="T4">Type of the fourth argument received by the expected invocation.</typeparam> + <seealso cref="M:Moq.Language.IRaise.Raises(Moq.MockedEvent,System.EventArgs)"/> + </member> + <member name="T:Moq.Language.IVerifies"> + <summary> + Defines the <c>Verifiable</c> verb. + </summary> + </member> + <member name="M:Moq.Language.IVerifies.Verifiable"> + <summary> + Marks the expectation as verifiable, meaning that a call + to <see cref="M:Moq.Mock.Verify"/> will check if this particular + expectation was met. + </summary> + <example> + The following example marks the expectation as verifiable: + <code> + mock.Expect(x => x.Execute("ping")) + .Returns(true) + .Verifiable(); + </code> + </example> + </member> + <member name="M:Moq.Language.IVerifies.Verifiable(System.String)"> + <summary> + Marks the expectation as verifiable, meaning that a call + to <see cref="M:Moq.Mock.Verify"/> will check if this particular + expectation was met, and specifies a message for failures. + </summary> + <example> + The following example marks the expectation as verifiable: + <code> + mock.Expect(x => x.Execute("ping")) + .Returns(true) + .Verifiable("Ping should be executed always!"); + </code> + </example> + </member> + <member name="T:Moq.MatcherAttribute"> + <summary> + Marks a method as a matcher, which allows complete replacement + of the built-in <see cref="T:Moq.It"/> class with your own argument + matching rules. + </summary> + <remarks> + <b>This feature has been deprecated in favor of the new + and simpler <see cref="T:Moq.Match`1"/>. + </b> + <para> + The argument matching is used to determine whether a concrete + invocation in the mock matches a given setup. This + matching mechanism is fully extensible. + </para> + <para> + There are two parts of a matcher: the compiler matcher + and the runtime matcher. + <list type="bullet"> + <item> + <term>Compiler matcher</term> + <description>Used to satisfy the compiler requirements for the + argument. Needs to be a method optionally receiving any arguments + you might need for the matching, but with a return type that + matches that of the argument. + <para> + Let's say I want to match a lists of orders that contains + a particular one. I might create a compiler matcher like the following: + </para> + <code> + public static class Orders + { + [Matcher] + public static IEnumerable<Order> Contains(Order order) + { + return null; + } + } + </code> + Now we can invoke this static method instead of an argument in an + invocation: + <code> + var order = new Order { ... }; + var mock = new Mock<IRepository<Order>>(); + + mock.Setup(x => x.Save(Orders.Contains(order))) + .Throws<ArgumentException>(); + </code> + Note that the return value from the compiler matcher is irrelevant. + This method will never be called, and is just used to satisfy the + compiler and to signal Moq that this is not a method that we want + to be invoked at runtime. + </description> + </item> + <item> + <term>Runtime matcher</term> + <description> + The runtime matcher is the one that will actually perform evaluation + when the test is run, and is defined by convention to have the + same signature as the compiler matcher, but where the return + value is the first argument to the call, which contains the + object received by the actual invocation at runtime: + <code> + public static bool Contains(IEnumerable<Order> orders, Order order) + { + return orders.Contains(order); + } + </code> + At runtime, the mocked method will be invoked with a specific + list of orders. This value will be passed to this runtime + matcher as the first argument, while the second argument is the + one specified in the setup (<c>x.Save(Orders.Contains(order))</c>). + <para> + The boolean returned determines whether the given argument has been + matched. If all arguments to the expected method are matched, then + the setup matches and is evaluated. + </para> + </description> + </item> + </list> + </para> + Using this extensible infrastructure, you can easily replace the entire + <see cref="T:Moq.It"/> set of matchers with your own. You can also avoid the + typical (and annoying) lengthy expressions that result when you have + multiple arguments that use generics. + </remarks> + <example> + The following is the complete example explained above: + <code> + public static class Orders + { + [Matcher] + public static IEnumerable<Order> Contains(Order order) + { + return null; + } + + public static bool Contains(IEnumerable<Order> orders, Order order) + { + return orders.Contains(order); + } + } + </code> + And the concrete test using this matcher: + <code> + var order = new Order { ... }; + var mock = new Mock<IRepository<Order>>(); + + mock.Setup(x => x.Save(Orders.Contains(order))) + .Throws<ArgumentException>(); + + // use mock, invoke Save, and have the matcher filter. + </code> + </example> + </member> + <member name="M:Moq.ExpressionExtensions.ToLambda(System.Linq.Expressions.Expression)"> + <summary> + Casts the expression to a lambda expression, removing + a cast if there's any. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.ToMethodCall(System.Linq.Expressions.LambdaExpression)"> + <summary> + Casts the body of the lambda expression to a <see cref="T:System.Linq.Expressions.MethodCallExpression"/>. + </summary> + <exception cref="T:System.ArgumentException">If the body is not a method call.</exception> + </member> + <member name="M:Moq.ExpressionExtensions.ToPropertyInfo(System.Linq.Expressions.LambdaExpression)"> + <summary> + Converts the body of the lambda expression into the <see cref="T:System.Reflection.PropertyInfo"/> referenced by it. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.IsProperty(System.Linq.Expressions.LambdaExpression)"> + <summary> + Checks whether the body of the lambda expression is a property access. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.IsProperty(System.Linq.Expressions.Expression)"> + <summary> + Checks whether the expression is a property access. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.IsPropertyIndexer(System.Linq.Expressions.LambdaExpression)"> + <summary> + Checks whether the body of the lambda expression is a property indexer, which is true + when the expression is an <see cref="T:System.Linq.Expressions.MethodCallExpression"/> whose + <see cref="P:System.Linq.Expressions.MethodCallExpression.Method"/> has <see cref="P:System.Reflection.MethodBase.IsSpecialName"/> + equal to <see langword="true"/>. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.IsPropertyIndexer(System.Linq.Expressions.Expression)"> + <summary> + Checks whether the expression is a property indexer, which is true + when the expression is an <see cref="T:System.Linq.Expressions.MethodCallExpression"/> whose + <see cref="P:System.Linq.Expressions.MethodCallExpression.Method"/> has <see cref="P:System.Reflection.MethodBase.IsSpecialName"/> + equal to <see langword="true"/>. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.CastTo``1(System.Linq.Expressions.Expression)"> + <summary> + Creates an expression that casts the given expression to the <typeparamref name="T"/> + type. + </summary> + </member> + <member name="M:Moq.ExpressionExtensions.ToStringFixed(System.Linq.Expressions.Expression)"> + <devdoc> + TODO: remove this code when https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=331583 + is fixed. + </devdoc> + </member> + <member name="T:Moq.ExpressionVisitor"> + <summary> + Base class for visitors of expression trees. + </summary> + <remarks> + <para>Provides the functionality of the internal visitor base class that + comes with Linq.</para> + <para>Matt's comments on the implementation:</para> + <para> + In this variant there is only one visitor class that dispatches calls to the general + Visit function out to specific VisitXXX methods corresponding to different node types. + Note not every node type gets it own method, for example all binary operators are + treated in one VisitBinary method. The nodes themselves do not directly participate + in the visitation process. They are treated as just data. + The reason for this is that the quantity of visitors is actually open ended. + You can write your own. Therefore no semantics of visiting is coupled into the node classes. + It’s all in the visitors. The default visit behavior for node XXX is baked into the base + class’s version of VisitXXX. + </para> + <para> + Another variant is that all VisitXXX methods return a node. + The Expression tree nodes are immutable. In order to change the tree you must construct + a new one. The default VisitXXX methods will construct a new node if any of its sub-trees change. + If no changes are made then the same node is returned. That way if you make a change + to a node (by making a new node) deep down in a tree, the rest of the tree is rebuilt + automatically for you. + </para> + See: http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx. + </remarks> + <author>Matt Warren: http://blogs.msdn.com/mattwar</author> + <contributor>Documented by InSTEDD: http://www.instedd.org</contributor> + </member> + <member name="M:Moq.ExpressionVisitor.#ctor"> + <summary> + Default constructor used by derived visitors. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.Expression"/>, determining which + of the concrete Visit methods to call. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitBinding(System.Linq.Expressions.MemberBinding)"> + <summary> + Visits the generic <see cref="T:System.Linq.Expressions.MemberBinding"/>, determining and + calling the appropriate Visit method according to the + <see cref="P:System.Linq.Expressions.MemberBinding.BindingType"/>, which will result + in calls to <see cref="M:Moq.ExpressionVisitor.VisitMemberAssignment(System.Linq.Expressions.MemberAssignment)"/>, + <see cref="M:Moq.ExpressionVisitor.VisitMemberMemberBinding(System.Linq.Expressions.MemberMemberBinding)"/> or <see cref="M:Moq.ExpressionVisitor.VisitMemberListBinding(System.Linq.Expressions.MemberListBinding)"/>. + </summary> + <param name="binding"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitElementInitializer(System.Linq.Expressions.ElementInit)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.ElementInit"/> initializer by + calling the <see cref="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"/> for the + <see cref="P:System.Linq.Expressions.ElementInit.Arguments"/>. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitUnary(System.Linq.Expressions.UnaryExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.UnaryExpression"/> expression by + calling <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.UnaryExpression.Operand"/> expression. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitBinary(System.Linq.Expressions.BinaryExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.BinaryExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.BinaryExpression.Left"/>, + <see cref="P:System.Linq.Expressions.BinaryExpression.Right"/> and <see cref="P:System.Linq.Expressions.BinaryExpression.Conversion"/> + expressions. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitTypeIs(System.Linq.Expressions.TypeBinaryExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.TypeBinaryExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.TypeBinaryExpression.Expression"/> + expression. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitConstant(System.Linq.Expressions.ConstantExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.ConstantExpression"/>, by default returning the + same <see cref="T:System.Linq.Expressions.ConstantExpression"/> without further behavior. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitConditional(System.Linq.Expressions.ConditionalExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.ConditionalExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.ConditionalExpression.Test"/>, + <see cref="P:System.Linq.Expressions.ConditionalExpression.IfTrue"/> and <see cref="P:System.Linq.Expressions.ConditionalExpression.IfFalse"/> + expressions. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitParameter(System.Linq.Expressions.ParameterExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.ParameterExpression"/> returning it + by default without further behavior. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMemberAccess(System.Linq.Expressions.MemberExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MemberExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.MemberExpression.Expression"/> + expression. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMethodCall(System.Linq.Expressions.MethodCallExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MethodCallExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.MethodCallExpression.Object"/> expression, + and then <see cref="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"/> with the <see cref="P:System.Linq.Expressions.MethodCallExpression.Arguments"/>. + </summary> + <param name="m"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"> + <summary> + Visits the <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1"/> by iterating + the list and visiting each <see cref="T:System.Linq.Expressions.Expression"/> in it. + </summary> + <param name="original"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMemberAssignment(System.Linq.Expressions.MemberAssignment)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MemberAssignment"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.MemberAssignment.Expression"/> expression. + </summary> + <param name="assignment"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMemberMemberBinding(System.Linq.Expressions.MemberMemberBinding)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MemberMemberBinding"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitBindingList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.MemberBinding})"/> with the <see cref="P:System.Linq.Expressions.MemberMemberBinding.Bindings"/>. + </summary> + <param name="binding"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMemberListBinding(System.Linq.Expressions.MemberListBinding)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MemberListBinding"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitElementInitializerList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.ElementInit})"/> with the + <see cref="P:System.Linq.Expressions.MemberListBinding.Initializers"/>. + </summary> + <param name="binding"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitBindingList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.MemberBinding})"> + <summary> + Visits the <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1"/> by + calling <see cref="M:Moq.ExpressionVisitor.VisitBinding(System.Linq.Expressions.MemberBinding)"/> for each <see cref="T:System.Linq.Expressions.MemberBinding"/> in the + collection. + </summary> + <param name="original"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitElementInitializerList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.ElementInit})"> + <summary> + Visits the <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1"/> by + calling <see cref="M:Moq.ExpressionVisitor.VisitElementInitializer(System.Linq.Expressions.ElementInit)"/> for each + <see cref="T:System.Linq.Expressions.ElementInit"/> in the collection. + </summary> + <param name="original"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitLambda(System.Linq.Expressions.LambdaExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.LambdaExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.Visit(System.Linq.Expressions.Expression)"/> with the <see cref="P:System.Linq.Expressions.LambdaExpression.Body"/> expression. + </summary> + <param name="lambda"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitNew(System.Linq.Expressions.NewExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.NewExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"/> with the <see cref="P:System.Linq.Expressions.NewExpression.Arguments"/> + expressions. + </summary> + <param name="nex"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitMemberInit(System.Linq.Expressions.MemberInitExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.MemberInitExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitNew(System.Linq.Expressions.NewExpression)"/> with the <see cref="P:System.Linq.Expressions.MemberInitExpression.NewExpression"/> + expression, then <see cref="M:Moq.ExpressionVisitor.VisitBindingList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.MemberBinding})"/> with the + <see cref="P:System.Linq.Expressions.MemberInitExpression.Bindings"/>. + </summary> + </member> + <member name="M:Moq.ExpressionVisitor.VisitListInit(System.Linq.Expressions.ListInitExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.ListInitExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitNew(System.Linq.Expressions.NewExpression)"/> with the <see cref="P:System.Linq.Expressions.ListInitExpression.NewExpression"/> + expression, and then <see cref="M:Moq.ExpressionVisitor.VisitElementInitializerList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.ElementInit})"/> with the + <see cref="P:System.Linq.Expressions.ListInitExpression.Initializers"/>. + </summary> + <param name="init"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitNewArray(System.Linq.Expressions.NewArrayExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.NewArrayExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"/> with the <see cref="P:System.Linq.Expressions.NewArrayExpression.Expressions"/> + expressions. + </summary> + <param name="na"></param> + <returns></returns> + </member> + <member name="M:Moq.ExpressionVisitor.VisitInvocation(System.Linq.Expressions.InvocationExpression)"> + <summary> + Visits the <see cref="T:System.Linq.Expressions.InvocationExpression"/> by calling + <see cref="M:Moq.ExpressionVisitor.VisitExpressionList(System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression})"/> with the <see cref="P:System.Linq.Expressions.InvocationExpression.Arguments"/> + expressions. + </summary> + <param name="iv"></param> + <returns></returns> + </member> + <member name="T:Moq.Evaluator"> + <summary> + Provides partial evaluation of subtrees, whenever they can be evaluated locally. + </summary> + <author>Matt Warren: http://blogs.msdn.com/mattwar</author> + <contributor>Documented by InSTEDD: http://www.instedd.org</contributor> + </member> + <member name="M:Moq.Evaluator.PartialEval(System.Linq.Expressions.Expression,System.Func{System.Linq.Expressions.Expression,System.Boolean})"> + <summary> + Performs evaluation and replacement of independent sub-trees + </summary> + <param name="expression">The root of the expression tree.</param> + <param name="fnCanBeEvaluated">A function that decides whether a given expression + node can be part of the local function.</param> + <returns>A new tree with sub-trees evaluated and replaced.</returns> + </member> + <member name="M:Moq.Evaluator.PartialEval(System.Linq.Expressions.Expression)"> + <summary> + Performs evaluation and replacement of independent sub-trees + </summary> + <param name="expression">The root of the expression tree.</param> + <returns>A new tree with sub-trees evaluated and replaced.</returns> + </member> + <member name="T:Moq.Evaluator.SubtreeEvaluator"> + <summary> + Evaluates and replaces sub-trees when first candidate is reached (top-down) + </summary> + </member> + <member name="T:Moq.Evaluator.Nominator"> + <summary> + Performs bottom-up analysis to determine which nodes can possibly + be part of an evaluated sub-tree. + </summary> + </member> + <member name="M:Guard.ArgumentNotNull(System.Object,System.String)"> + <summary> + Checks an argument to ensure it isn't null. + </summary> + <param name="value">The argument value to check.</param> + <param name="argumentName">The name of the argument.</param> + </member> + <member name="M:Guard.ArgumentNotNullOrEmptyString(System.String,System.String)"> + <summary> + Checks a string argument to ensure it isn't null or empty. + </summary> + <param name="argumentValue">The argument value to check.</param> + <param name="argumentName">The name of the argument.</param> + </member> + <member name="M:Guard.ArgumentNotOutOfRangeInclusive``1(``0,``0,``0,System.String)"> + <summary> + Checks an argument to ensure it is in the specified range including the edges. + </summary> + <typeparam name="T">Type of the argument to check, it must be an <see cref="T:System.IComparable"/> type. + </typeparam> + <param name="value">The argument value to check.</param> + <param name="from">The minimun allowed value for the argument.</param> + <param name="to">The maximun allowed value for the argument.</param> + <param name="argumentName">The name of the argument.</param> + </member> + <member name="M:Guard.ArgumentNotOutOfRangeExclusive``1(``0,``0,``0,System.String)"> + <summary> + Checks an argument to ensure it is in the specified range excluding the edges. + </summary> + <typeparam name="T">Type of the argument to check, it must be an <see cref="T:System.IComparable"/> type. + </typeparam> + <param name="value">The argument value to check.</param> + <param name="from">The minimun allowed value for the argument.</param> + <param name="to">The maximun allowed value for the argument.</param> + <param name="argumentName">The name of the argument.</param> + </member> + <member name="T:Moq.Language.IReturnsGetter`2"> + <summary> + Defines the <c>Returns</c> verb for property get setups. + </summary> + <typeparam name="TMock">Mocked type.</typeparam> + <typeparam name="TProperty">Type of the property.</typeparam> + </member> + <member name="M:Moq.Language.IReturnsGetter`2.Returns(`1)"> + <summary> + Specifies the value to return. + </summary> + <param name="value">The value to return, or <see langword="null"/>.</param> + <example> + Return a <c>true</c> value from the property getter call: + <code> + mock.SetupGet(x => x.Suspended) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Language.IReturnsGetter`2.Returns(System.Func{`1})"> + <summary> + Specifies a function that will calculate the value to return for the property. + </summary> + <param name="valueFunction">The function that will calculate the return value.</param> + <example> + Return a calculated value when the property is retrieved: + <code> + mock.SetupGet(x => x.Suspended) + .Returns(() => returnValues[0]); + </code> + The lambda expression to retrieve the return value is lazy-executed, + meaning that its value may change depending on the moment the property + is retrieved and the value the <c>returnValues</c> array has at + that moment. + </example> + </member> + <member name="T:Moq.Language.ICallbackGetter`2"> + <summary> + Defines the <c>Callback</c> verb for property getter setups. + </summary> + <seealso cref="M:Moq.Mock`1.SetupGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"/> + <typeparam name="TMock">Mocked type.</typeparam> + <typeparam name="TProperty">Type of the property.</typeparam> + </member> + <member name="M:Moq.Language.ICallbackGetter`2.Callback(System.Action)"> + <summary> + Specifies a callback to invoke when the property is retrieved. + </summary> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the property value being set. + <code> + mock.SetupGet(x => x.Suspended) + .Callback(() => called = true) + .Returns(true); + </code> + </example> + </member> + <member name="T:Moq.Language.Flow.IThrowsResult"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.Flow.IReturnsThrows`2"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.IReturns`2"> + <summary> + Defines the <c>Returns</c> verb. + </summary> + <typeparam name="TMock">Mocked type.</typeparam> + <typeparam name="TResult">Type of the return value from the expression.</typeparam> + </member> + <member name="M:Moq.Language.IReturns`2.Returns(`1)"> + <summary> + Specifies the value to return. + </summary> + <param name="value">The value to return, or <see langword="null"/>.</param> + <example> + Return a <c>true</c> value from the method call: + <code> + mock.Setup(x => x.Execute("ping")) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Language.IReturns`2.Returns(System.Func{`1})"> + <summary> + Specifies a function that will calculate the value to return from the method. + </summary> + <param name="valueFunction">The function that will calculate the return value.</param> + <example group="returns"> + Return a calculated value when the method is called: + <code> + mock.Setup(x => x.Execute("ping")) + .Returns(() => returnValues[0]); + </code> + The lambda expression to retrieve the return value is lazy-executed, + meaning that its value may change depending on the moment the method + is executed and the value the <c>returnValues</c> array has at + that moment. + </example> + </member> + <member name="M:Moq.Language.IReturns`2.Returns``1(System.Func{``0,`1})"> + <summary> + Specifies a function that will calculate the value to return from the method, + retrieving the arguments for the invocation. + </summary> + <typeparam name="T">Type of the argument of the invoked method.</typeparam> + <param name="valueFunction">The function that will calculate the return value.</param> + <example group="returns"> + Return a calculated value which is evaluated lazily at the time of the invocation. + <para> + The lookup list can change between invocations and the setup + will return different values accordingly. Also, notice how the specific + string argument is retrieved by simply declaring it as part of the lambda + expression: + </para> + <code> + mock.Setup(x => x.Execute(It.IsAny<string>())) + .Returns((string command) => returnValues[command]); + </code> + </example> + </member> + <member name="M:Moq.Language.IReturns`2.Returns``2(System.Func{``0,``1,`1})"> + <summary> + Specifies a function that will calculate the value to return from the method, + retrieving the arguments for the invocation. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <param name="valueFunction">The function that will calculate the return value.</param> + <example group="returns"> + Return a calculated value which is evaluated lazily at the time of the invocation. + <para> + The return value is calculated from the value of the actual method invocation arguments. + Notice how the arguments are retrieved by simply declaring them as part of the lambda + expression: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>())) + .Returns((string arg1, string arg2) => arg1 + arg2); + </code> + </example> + </member> + <member name="M:Moq.Language.IReturns`2.Returns``3(System.Func{``0,``1,``2,`1})"> + <summary> + Specifies a function that will calculate the value to return from the method, + retrieving the arguments for the invocation. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <param name="valueFunction">The function that will calculate the return value.</param> + <example group="returns"> + Return a calculated value which is evaluated lazily at the time of the invocation. + <para> + The return value is calculated from the value of the actual method invocation arguments. + Notice how the arguments are retrieved by simply declaring them as part of the lambda + expression: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>())) + .Returns((string arg1, string arg2, int arg3) => arg1 + arg2 + arg3); + </code> + </example> + </member> + <member name="M:Moq.Language.IReturns`2.Returns``4(System.Func{``0,``1,``2,``3,`1})"> + <summary> + Specifies a function that will calculate the value to return from the method, + retrieving the arguments for the invocation. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <typeparam name="T4">Type of the fourth argument of the invoked method.</typeparam> + <param name="valueFunction">The function that will calculate the return value.</param> + <example group="returns"> + Return a calculated value which is evaluated lazily at the time of the invocation. + <para> + The return value is calculated from the value of the actual method invocation arguments. + Notice how the arguments are retrieved by simply declaring them as part of the lambda + expression: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>(), + It.IsAny<bool>())) + .Returns((string arg1, string arg2, int arg3, bool arg4) => arg1 + arg2 + arg3 + arg4); + </code> + </example> + </member> + <member name="T:Moq.Language.IThrows"> + <summary> + Defines the <c>Throws</c> verb. + </summary> + </member> + <member name="M:Moq.Language.IThrows.Throws(System.Exception)"> + <summary> + Specifies the exception to throw when the method is invoked. + </summary> + <param name="exception">Exception instance to throw.</param> + <example> + This example shows how to throw an exception when the method is + invoked with an empty string argument: + <code> + mock.Setup(x => x.Execute("")) + .Throws(new ArgumentException()); + </code> + </example> + </member> + <member name="M:Moq.Language.IThrows.Throws``1"> + <summary> + Specifies the type of exception to throw when the method is invoked. + </summary> + <typeparam name="TException">Type of exception to instantiate and throw when the setup is matched.</typeparam> + <example> + This example shows how to throw an exception when the method is + invoked with an empty string argument: + <code> + mock.Setup(x => x.Execute("")) + .Throws<ArgumentException>(); + </code> + </example> + </member> + <member name="T:Moq.Language.Flow.IReturnsThrowsGetter`2"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.Flow.ICallbackResult"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.ICallback`2"> + <summary> + Defines the <c>Callback</c> verb and overloads for callbacks on + setups that return a value. + </summary> + <typeparam name="TMock">Mocked type.</typeparam> + <typeparam name="TResult">Type of the return value of the setup.</typeparam> + </member> + <member name="M:Moq.Language.ICallback`2.Callback(System.Action)"> + <summary> + Specifies a callback to invoke when the method is called. + </summary> + <param name="action">Callback method to invoke.</param> + <example> + The following example specifies a callback to set a boolean + value that can be used later: + <code> + bool called = false; + mock.Setup(x => x.Execute()) + .Callback(() => called = true) + .Returns(true); + </code> + Note that in the case of value-returning methods, after the <c>Callback</c> + call you can still specify the return value. + </example> + </member> + <member name="M:Moq.Language.ICallback`2.Callback``1(System.Action{``0})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T">Type of the argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation argument value. + <para> + Notice how the specific string argument is retrieved by simply declaring + it as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute(It.IsAny<string>())) + .Callback((string command) => Console.WriteLine(command)) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback`2.Callback``2(System.Action{``0,``1})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>())) + .Callback((string arg1, string arg2) => Console.WriteLine(arg1 + arg2)) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback`2.Callback``3(System.Action{``0,``1,``2})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>())) + .Callback((string arg1, string arg2, int arg3) => Console.WriteLine(arg1 + arg2 + arg3)) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Language.ICallback`2.Callback``4(System.Action{``0,``1,``2,``3})"> + <summary> + Specifies a callback to invoke when the method is called that receives the original + arguments. + </summary> + <typeparam name="T1">Type of the first argument of the invoked method.</typeparam> + <typeparam name="T2">Type of the second argument of the invoked method.</typeparam> + <typeparam name="T3">Type of the third argument of the invoked method.</typeparam> + <typeparam name="T4">Type of the fourth argument of the invoked method.</typeparam> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the concrete invocation arguments values. + <para> + Notice how the specific arguments are retrieved by simply declaring + them as part of the lambda expression for the callback: + </para> + <code> + mock.Setup(x => x.Execute( + It.IsAny<string>(), + It.IsAny<string>(), + It.IsAny<int>(), + It.IsAny<bool>())) + .Callback((string arg1, string arg2, int arg3, bool arg4) => Console.WriteLine(arg1 + arg2 + arg3 + arg4)) + .Returns(true); + </code> + </example> + </member> + <member name="T:Moq.IMocked`1"> + <summary> + Implemented by all generated mock object instances. + </summary> + </member> + <member name="T:Moq.IMocked"> + <summary> + Implemented by all generated mock object instances. + </summary> + </member> + <member name="P:Moq.IMocked.Mock"> + <summary> + Reference the Mock that contains this as the <c>mock.Object</c> value. + </summary> + </member> + <member name="P:Moq.IMocked`1.Mock"> + <summary> + Reference the Mock that contains this as the <c>mock.Object</c> value. + </summary> + </member> + <member name="T:Moq.Interceptor"> + <summary> + Implements the actual interception and method invocation for + all mocks. + </summary> + </member> + <member name="M:Moq.Interceptor.GetEventFromName(System.String)"> + <summary> + Get an eventInfo for a given event name. Search type ancestors depth first if necessary. + </summary> + <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param> + </member> + <member name="M:Moq.Interceptor.GetAncestorTypes(System.Type)"> + <summary> + Given a type return all of its ancestors, both types and interfaces. + </summary> + <param name="initialType">The type to find immediate ancestors of</param> + </member> + <member name="T:Moq.Language.Flow.ISetup`1"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.INever"> + <summary> + Defines the <c>Never</c> verb. + </summary> + </member> + <member name="M:Moq.Language.INever.Never"> + <summary> + The expected invocation is never expected to happen. + </summary> + <example> + <code> + var mock = new Mock<ICommand>(); + mock.Setup(foo => foo.Execute("ping")) + .Never(); + </code> + </example> + <remarks> + <see cref="M:Moq.Language.INever.Never"/> is always verified inmediately as + the invocations are performed, like strict mocks do + with unexpected invocations. + </remarks> + </member> + <member name="T:Moq.Language.Flow.ISetup`2"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.Flow.ISetupGetter`2"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.Flow.ISetupSetter`2"> + <summary> + Implements the fluent API. + </summary> + </member> + <member name="T:Moq.Language.ICallbackSetter`1"> + <summary> + Defines the <c>Callback</c> verb for property setter setups. + </summary> + <typeparam name="TProperty">Type of the property.</typeparam> + </member> + <member name="M:Moq.Language.ICallbackSetter`1.Callback(System.Action{`0})"> + <summary> + Specifies a callback to invoke when the property is set that receives the + property value being set. + </summary> + <param name="action">Callback method to invoke.</param> + <example> + Invokes the given callback with the property value being set. + <code> + mock.SetupSet(x => x.Suspended) + .Callback((bool state) => Console.WriteLine(state)); + </code> + </example> + </member> + <member name="T:Moq.It"> + <summary> + Allows the specification of a matching condition for an + argument in a method invocation, rather than a specific + argument value. "It" refers to the argument being matched. + </summary><remarks> + This class allows the setup to match a method invocation + with an arbitrary value, with a value in a specified range, or + even one that matches a given predicate. + </remarks> + </member> + <member name="M:Moq.It.IsAny``1"> + <summary> + Matches any value of the given <paramref name="TValue"/> type. + </summary><remarks> + Typically used when the actual argument value for a method + call is not relevant. + </remarks><example> + <code> + // Throws an exception for a call to Remove with any string value. + mock.Setup(x => x.Remove(It.IsAny<string>())).Throws(new InvalidOperationException()); + </code> + </example><typeparam name="TValue">Type of the value.</typeparam> + </member> + <member name="M:Moq.It.Is``1(System.Linq.Expressions.Expression{System.Predicate{``0}})"> + <summary> + Matches any value that satisfies the given predicate. + </summary><typeparam name="TValue">Type of the argument to check.</typeparam><param name="match">The predicate used to match the method argument.</param><remarks> + Allows the specification of a predicate to perform matching + of method call arguments. + </remarks><example> + This example shows how to return the value <c>1</c> whenever the argument to the + <c>Do</c> method is an even number. + <code> + mock.Setup(x => x.Do(It.Is<int>(i => i % 2 == 0))) + .Returns(1); + </code> + This example shows how to throw an exception if the argument to the + method is a negative number: + <code> + mock.Setup(x => x.GetUser(It.Is<int>(i => i < 0))) + .Throws(new ArgumentException()); + </code> + </example> + </member> + <member name="M:Moq.It.IsInRange``1(``0,``0,Moq.Range)"> + <summary> + Matches any value that is in the range specified. + </summary><typeparam name="TValue">Type of the argument to check.</typeparam><param name="from">The lower bound of the range.</param><param name="to">The upper bound of the range.</param><param name="rangeKind"> + The kind of range. See <see cref="T:Moq.Range"/>. + </param><example> + The following example shows how to expect a method call + with an integer argument within the 0..100 range. + <code> + mock.Setup(x => x.HasInventory( + It.IsAny<string>(), + It.IsInRange(0, 100, Range.Inclusive))) + .Returns(false); + </code> + </example> + </member> + <member name="M:Moq.It.IsRegex(System.String)"> + <summary> + Matches a string argument if it matches the given regular expression pattern. + </summary><param name="regex">The pattern to use to match the string argument value.</param><example> + The following example shows how to expect a call to a method where the + string argument matches the given regular expression: + <code> + mock.Setup(x => x.Check(It.IsRegex("[a-z]+"))).Returns(1); + </code> + </example> + </member> + <member name="M:Moq.It.IsRegex(System.String,System.Text.RegularExpressions.RegexOptions)"> + <summary> + Matches a string argument if it matches the given regular expression pattern. + </summary><param name="regex">The pattern to use to match the string argument value.</param><param name="options">The options used to interpret the pattern.</param><example> + The following example shows how to expect a call to a method where the + string argument matches the given regular expression, in a case insensitive way: + <code> + mock.Setup(x => x.Check(It.IsRegex("[a-z]+", RegexOptions.IgnoreCase))).Returns(1); + </code> + </example> + </member> + <member name="T:Moq.Matchers.MatcherAttributeMatcher"> + <summary> + Matcher to treat static functions as matchers. + + mock.Setup(x => x.StringMethod(A.MagicString())); + + pbulic static class A + { + [Matcher] + public static string MagicString() { return null; } + public static bool MagicString(string arg) + { + return arg == "magic"; + } + } + + Will success if: mock.Object.StringMethod("magic"); + and fail with any other call. + </summary> + </member> + <member name="T:Moq.MethodCallReturn"> + <devdoc> + We need this non-generics base class so that + we can use <see cref="P:Moq.MethodCallReturn.HasReturnValue"/> from + generic code. + </devdoc> + </member> + <member name="T:Moq.Mock"> + <summary> + Base class for mocks and static helper class with methods that + apply to mocked objects, such as <see cref="M:Moq.Mock.Get``1(``0)"/> to + retrieve a <see cref="T:Moq.Mock`1"/> from an object instance. + </summary> + </member> + <member name="M:Moq.Mock.Get``1(``0)"> + <summary> + Retrieves the mock object for the given object instance. + </summary><typeparam name="T"> + Type of the mock to retrieve. Can be omitted as it's inferred + from the object instance passed in as the <paramref name="mocked"/> instance. + </typeparam><param name="mocked">The instance of the mocked object.</param><returns>The mock associated with the mocked object.</returns><exception cref="T:System.ArgumentException"> + The received <paramref name="mocked"/> instance + was not created by Moq. + </exception><example group="advanced"> + The following example shows how to add a new setup to an object + instance which is not the original <see cref="T:Moq.Mock`1"/> but rather + the object associated with it: + <code> + // Typed instance, not the mock, is retrieved from some test API. + HttpContextBase context = GetMockContext(); + + // context.Request is the typed object from the "real" API + // so in order to add a setup to it, we need to get + // the mock that "owns" it + Mock<HttpRequestBase> request = Mock.Get(context.Request); + mock.Setup(req => req.AppRelativeCurrentExecutionFilePath) + .Returns(tempUrl); + </code> + </example> + </member> + <member name="M:Moq.Mock.GetObject"> + <summary> + Returns the mocked object value. + </summary> + </member> + <member name="M:Moq.Mock.Verify"> + <summary> + Verifies that all verifiable expectations have been met. + </summary><example group="verification"> + This example sets up an expectation and marks it as verifiable. After + the mock is used, a <c>Verify()</c> call is issued on the mock + to ensure the method in the setup was invoked: + <code> + var mock = new Mock<IWarehouse>(); + this.Setup(x => x.HasInventory(TALISKER, 50)).Verifiable().Returns(true); + ... + // other test code + ... + // Will throw if the test code has didn't call HasInventory. + this.Verify(); + </code> + </example><exception cref="T:Moq.MockException">Not all verifiable expectations were met.</exception> + </member> + <member name="M:Moq.Mock.VerifyAll"> + <summary> + Verifies all expectations regardless of whether they have + been flagged as verifiable. + </summary><example group="verification"> + This example sets up an expectation without marking it as verifiable. After + the mock is used, a <see cref="M:Moq.Mock.VerifyAll"/> call is issued on the mock + to ensure that all expectations are met: + <code> + var mock = new Mock<IWarehouse>(); + this.Setup(x => x.HasInventory(TALISKER, 50)).Returns(true); + ... + // other test code + ... + // Will throw if the test code has didn't call HasInventory, even + // that expectation was not marked as verifiable. + this.VerifyAll(); + </code> + </example><exception cref="T:Moq.MockException">At least one expectation was not met.</exception> + </member> + <member name="M:Moq.Mock.GetInterceptor(System.Linq.Expressions.LambdaExpression,Moq.Mock)"> + <summary> + Gets the interceptor target for the given expression and root mock, + building the intermediate hierarchy of mock objects if necessary. + </summary> + </member> + <member name="M:Moq.Mock.CreateEventHandler``1"> + <summary> + Creates a handler that can be associated to an event receiving + the given <typeparamref name="TEventArgs"/> and can be used + to raise the event. + </summary><typeparam name="TEventArgs"> + Type of <see cref="T:System.EventArgs"/> + data passed in to the event. + </typeparam><example> + This example shows how to invoke an event with a custom event arguments + class in a view that will cause its corresponding presenter to + react by changing its state: + <code> + var mockView = new Mock<IOrdersView>(); + var mockedEvent = mockView.CreateEventHandler<OrderEventArgs>(); + + var presenter = new OrdersPresenter(mockView.Object); + + // Check that the presenter has no selection by default + Assert.Null(presenter.SelectedOrder); + + // Create a mock event handler of the appropriate type + var handler = mockView.CreateEventHandler<OrderEventArgs>(); + // Associate it with the event we want to raise + mockView.Object.Cancel += handler; + // Finally raise the event with a specific arguments data + handler.Raise(new OrderEventArgs { Order = new Order("moq", 500) }); + + // Now the presenter reacted to the event, and we have a selected order + Assert.NotNull(presenter.SelectedOrder); + Assert.Equal("moq", presenter.SelectedOrder.ProductName); + </code> + </example> + </member> + <member name="M:Moq.Mock.CreateEventHandler"> + <summary> + Creates a handler that can be associated to an event receiving + a generic <see cref="T:System.EventArgs"/> and can be used + to raise the event. + </summary><example> + This example shows how to invoke a generic event in a view that will + cause its corresponding presenter to react by changing its state: + <code> + var mockView = new Mock<IOrdersView>(); + var mockedEvent = mockView.CreateEventHandler(); + + var presenter = new OrdersPresenter(mockView.Object); + + // Check that the presenter is not in the "Canceled" state + Assert.False(presenter.IsCanceled); + + // Create a mock event handler of the appropriate type + var handler = mockView.CreateEventHandler(); + // Associate it with the event we want to raise + mockView.Object.Cancel += handler; + // Finally raise the event + handler.Raise(EventArgs.Empty); + + // Now the presenter reacted to the event, and changed its state + Assert.True(presenter.IsCanceled); + </code> + </example> + </member> + <member name="M:Moq.Mock.Moq#IHideObjectMembers#GetType"> + <summary> + Base class for mocks and static helper class with methods that + apply to mocked objects, such as <see cref="M:Moq.Mock.Get``1(``0)"/> to + retrieve a <see cref="T:Moq.Mock`1"/> from an object instance. + </summary> + </member> + <member name="P:Moq.Mock.Behavior"> + <summary> + Behavior of the mock, according to the value set in the constructor. + </summary> + </member> + <member name="P:Moq.Mock.CallBase"> + <summary> + Whether the base member virtual implementation will be called + for mocked classes if no setup is matched. Defaults to <see langword="false"/>. + </summary> + </member> + <member name="P:Moq.Mock.DefaultValue"> + <summary> + Specifies the behavior to use when returning default values for + unexpected invocations on loose mocks. + </summary> + </member> + <member name="P:Moq.Mock.Object"> + <summary> + Gets the mocked object instance, which is of the mocked type <typeparamref name="T"/>. + </summary> + </member> + <member name="P:Moq.Mock.MockedType"> + <summary> + Retrieves the type of the mocked object, its generic type argument. + This is used in the auto-mocking of hierarchy access. + </summary> + </member> + <member name="P:Moq.Mock.DefaultValueProvider"> + <summary> + Specifies the class that will determine the default + value to return when invocations are made that + have no setups and need to return a default + value (for loose mocks). + </summary> + </member> + <member name="P:Moq.Mock.ImplementedInterfaces"> + <summary> + Exposes the list of extra interfaces implemented by the mock. + </summary> + </member> + <member name="T:Moq.MockBehavior"> + <summary> + Options to customize the behavior of the mock. + </summary> + </member> + <member name="F:Moq.MockBehavior.Strict"> + <summary> + Causes the mock to always throw + an exception for invocations that don't have a + corresponding setup. + </summary> + </member> + <member name="F:Moq.MockBehavior.Loose"> + <summary> + Will never throw exceptions, returning default + values when necessary (null for reference types, + zero for value types or empty enumerables and arrays). + </summary> + </member> + <member name="F:Moq.MockBehavior.Default"> + <summary> + Default mock behavior, which equals <see cref="F:Moq.MockBehavior.Loose"/>. + </summary> + </member> + <member name="T:Moq.MockedEvent"> + <summary> + Represents a generic event that has been mocked and can + be rised. + </summary> + </member> + <member name="M:Moq.MockedEvent.Handle(System.Object,System.EventArgs)"> + <summary> + Provided solely to allow the interceptor to determine when the attached + handler is coming from this mocked event so we can assign the + corresponding EventInfo for it. + </summary> + </member> + <member name="M:Moq.MockedEvent.DoRaise(System.EventArgs)"> + <summary> + Raises the associated event with the given + event argument data. + </summary> + </member> + <member name="M:Moq.MockedEvent.DoRaise(System.Object[])"> + <summary> + Raises the associated event with the given + event argument data. + </summary> + </member> + <member name="M:Moq.MockedEvent.op_Implicit(Moq.MockedEvent)~System.EventHandler"> + <summary> + Provides support for attaching a <see cref="T:Moq.MockedEvent"/> to + a generic <see cref="T:System.EventHandler"/> event. + </summary> + <param name="mockEvent">Event to convert.</param> + </member> + <member name="E:Moq.MockedEvent.Raised"> + <summary> + Event raised whenever the mocked event is rised. + </summary> + </member> + <member name="T:Moq.MockException"> + <summary> + Exception thrown by mocks when setups are not matched, + the mock is not properly setup, etc. + </summary> + <remarks> + A distinct exception type is provided so that exceptions + thrown by the mock can be differentiated in tests that + expect other exceptions to be thrown (i.e. ArgumentException). + <para> + Richer exception hierarchy/types are not provided as + tests typically should <b>not</b> catch or expect exceptions + from the mocks. These are typically the result of changes + in the tested class or its collaborators implementation, and + result in fixes in the mock setup so that they dissapear and + allow the test to pass. + </para> + </remarks> + </member> + <member name="M:Moq.MockException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)"> + <summary> + Supports the serialization infrastructure. + </summary> + <param name="info">Serialization information.</param> + <param name="context">Streaming context.</param> + </member> + <member name="M:Moq.MockException.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)"> + <summary> + Supports the serialization infrastructure. + </summary> + <param name="info">Serialization information.</param> + <param name="context">Streaming context.</param> + </member> + <member name="T:Moq.MockException.ExceptionReason"> + <summary> + Made internal as it's of no use for + consumers, but it's important for + our own tests. + </summary> + </member> + <member name="T:Moq.MockVerificationException"> + <devdoc> + Used by the mock factory to accumulate verification + failures. + </devdoc> + </member> + <member name="M:Moq.MockVerificationException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)"> + <summary> + Supports the serialization infrastructure. + </summary> + </member> + <member name="T:Moq.MockFactory"> + <summary> + Utility factory class to use to construct multiple + mocks when consistent verification is + desired for all of them. + </summary> + <remarks> + If multiple mocks will be created during a test, passing + the desired <see cref="T:Moq.MockBehavior"/> (if different than the + <see cref="F:Moq.MockBehavior.Default"/> or the one + passed to the factory constructor) and later verifying each + mock can become repetitive and tedious. + <para> + This factory class helps in that scenario by providing a + simplified creation of multiple mocks with a default + <see cref="T:Moq.MockBehavior"/> (unless overriden by calling + <see cref="M:Moq.MockFactory.Create``1(Moq.MockBehavior)"/>) and posterior verification. + </para> + </remarks> + <example group="factory"> + The following is a straightforward example on how to + create and automatically verify strict mocks using a <see cref="T:Moq.MockFactory"/>: + <code> + var factory = new MockFactory(MockBehavior.Strict); + + var foo = factory.Create<IFoo>(); + var bar = factory.Create<IBar>(); + + // no need to call Verifiable() on the setup + // as we'll be validating all of them anyway. + foo.Setup(f => f.Do()); + bar.Setup(b => b.Redo()); + + // exercise the mocks here + + factory.VerifyAll(); + // At this point all setups are already checked + // and an optional MockException might be thrown. + // Note also that because the mocks are strict, any invocation + // that doesn't have a matching setup will also throw a MockException. + </code> + The following examples shows how to setup the factory + to create loose mocks and later verify only verifiable setups: + <code> + var factory = new MockFactory(MockBehavior.Loose); + + var foo = factory.Create<IFoo>(); + var bar = factory.Create<IBar>(); + + // this setup will be verified when we verify the factory + foo.Setup(f => f.Do()).Verifiable(); + + // this setup will NOT be verified + foo.Setup(f => f.Calculate()); + + // this setup will be verified when we verify the factory + bar.Setup(b => b.Redo()).Verifiable(); + + // exercise the mocks here + // note that because the mocks are Loose, members + // called in the interfaces for which no matching + // setups exist will NOT throw exceptions, + // and will rather return default values. + + factory.Verify(); + // At this point verifiable setups are already checked + // and an optional MockException might be thrown. + </code> + The following examples shows how to setup the factory with a + default strict behavior, overriding that default for a + specific mock: + <code> + var factory = new MockFactory(MockBehavior.Strict); + + // this particular one we want loose + var foo = factory.Create<IFoo>(MockBehavior.Loose); + var bar = factory.Create<IBar>(); + + // specify setups + + // exercise the mocks here + + factory.Verify(); + </code> + </example> + <seealso cref="T:Moq.MockBehavior"/> + </member> + <member name="M:Moq.MockFactory.#ctor(Moq.MockBehavior)"> + <summary> + Initializes the factory with the given <paramref name="defaultBehavior"/> + for newly created mocks from the factory. + </summary> + <param name="defaultBehavior">The behavior to use for mocks created + using the <see cref="M:Moq.MockFactory.Create``1"/> factory method if not overriden + by using the <see cref="M:Moq.MockFactory.Create``1(Moq.MockBehavior)"/> overload.</param> + </member> + <member name="M:Moq.MockFactory.Create``1"> + <summary> + Creates a new mock with the default <see cref="T:Moq.MockBehavior"/> + specified at factory construction time. + </summary> + <typeparam name="T">Type to mock.</typeparam> + <returns>A new <see cref="T:Moq.Mock`1"/>.</returns> + <example ignore="true"> + <code> + var factory = new MockFactory(MockBehavior.Strict); + + var foo = factory.Create<IFoo>(); + // use mock on tests + + factory.VerifyAll(); + </code> + </example> + </member> + <member name="M:Moq.MockFactory.Create``1(System.Object[])"> + <summary> + Creates a new mock with the default <see cref="T:Moq.MockBehavior"/> + specified at factory construction time and with the + the given constructor arguments for the class. + </summary> + <remarks> + The mock will try to find the best match constructor given the + constructor arguments, and invoke that to initialize the instance. + This applies only to classes, not interfaces. + </remarks> + <typeparam name="T">Type to mock.</typeparam> + <param name="args">Constructor arguments for mocked classes.</param> + <returns>A new <see cref="T:Moq.Mock`1"/>.</returns> + <example ignore="true"> + <code> + var factory = new MockFactory(MockBehavior.Default); + + var mock = factory.Create<MyBase>("Foo", 25, true); + // use mock on tests + + factory.Verify(); + </code> + </example> + </member> + <member name="M:Moq.MockFactory.Create``1(Moq.MockBehavior)"> + <summary> + Creates a new mock with the given <paramref name="behavior"/>. + </summary> + <typeparam name="T">Type to mock.</typeparam> + <param name="behavior">Behavior to use for the mock, which overrides + the default behavior specified at factory construction time.</param> + <returns>A new <see cref="T:Moq.Mock`1"/>.</returns> + <example group="factory"> + The following example shows how to create a mock with a different + behavior to that specified as the default for the factory: + <code> + var factory = new MockFactory(MockBehavior.Strict); + + var foo = factory.Create<IFoo>(MockBehavior.Loose); + </code> + </example> + </member> + <member name="M:Moq.MockFactory.Create``1(Moq.MockBehavior,System.Object[])"> + <summary> + Creates a new mock with the given <paramref name="behavior"/> + and with the the given constructor arguments for the class. + </summary> + <remarks> + The mock will try to find the best match constructor given the + constructor arguments, and invoke that to initialize the instance. + This applies only to classes, not interfaces. + </remarks> + <typeparam name="T">Type to mock.</typeparam> + <param name="behavior">Behavior to use for the mock, which overrides + the default behavior specified at factory construction time.</param> + <param name="args">Constructor arguments for mocked classes.</param> + <returns>A new <see cref="T:Moq.Mock`1"/>.</returns> + <example group="factory"> + The following example shows how to create a mock with a different + behavior to that specified as the default for the factory, passing + constructor arguments: + <code> + var factory = new MockFactory(MockBehavior.Default); + + var mock = factory.Create<MyBase>(MockBehavior.Strict, "Foo", 25, true); + </code> + </example> + </member> + <member name="M:Moq.MockFactory.CreateMock``1(Moq.MockBehavior,System.Object[])"> + <summary> + Implements creation of a new mock within the factory. + </summary> + <typeparam name="T">Type to mock.</typeparam> + <param name="behavior">The behavior for the new mock.</param> + <param name="args">Optional arguments for the construction of the mock.</param> + </member> + <member name="M:Moq.MockFactory.Verify"> + <summary> + Verifies all verifiable expectations on all mocks created + by this factory. + </summary> + <seealso cref="M:Moq.Mock.Verify"/> + <exception cref="T:Moq.MockException">One or more mocks had expectations that were not satisfied.</exception> + </member> + <member name="M:Moq.MockFactory.VerifyAll"> + <summary> + Verifies all verifiable expectations on all mocks created + by this factory. + </summary> + <seealso cref="M:Moq.Mock.Verify"/> + <exception cref="T:Moq.MockException">One or more mocks had expectations that were not satisfied.</exception> + </member> + <member name="M:Moq.MockFactory.VerifyMocks(System.Action{Moq.Mock})"> + <summary> + Invokes <paramref name="verifyAction"/> for each mock + in <see cref="P:Moq.MockFactory.Mocks"/>, and accumulates the resulting + <see cref="T:Moq.MockVerificationException"/> that might be + thrown from the action. + </summary> + <param name="verifyAction">The action to execute against + each mock.</param> + </member> + <member name="P:Moq.MockFactory.CallBase"> + <summary> + Whether the base member virtual implementation will be called + for mocked classes if no setup is matched. Defaults to <see langword="false"/>. + </summary> + </member> + <member name="P:Moq.MockFactory.DefaultValue"> + <summary> + Specifies the behavior to use when returning default values for + unexpected invocations on loose mocks. + </summary> + </member> + <member name="P:Moq.MockFactory.Mocks"> + <summary> + Gets the mocks that have been created by this factory and + that will get verified together. + </summary> + </member> + <member name="T:Moq.Properties.Resources"> + <summary> + A strongly-typed resource class, for looking up localized strings, etc. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ResourceManager"> + <summary> + Returns the cached ResourceManager instance used by this class. + </summary> + </member> + <member name="P:Moq.Properties.Resources.Culture"> + <summary> + Overrides the current thread's CurrentUICulture property for all + resource lookups using this strongly typed resource class. + </summary> + </member> + <member name="P:Moq.Properties.Resources.AlreadyInitialized"> + <summary> + Looks up a localized string similar to Mock type has already been initialized by accessing its Object property. Adding interfaces must be done before that.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ArgumentCannotBeEmpty"> + <summary> + Looks up a localized string similar to Value cannot be an empty string.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.AsMustBeInterface"> + <summary> + Looks up a localized string similar to Can only add interfaces to the mock.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.CantSetReturnValueForVoid"> + <summary> + Looks up a localized string similar to Can't set return value for void method {0}.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ConstructorArgsForInterface"> + <summary> + Looks up a localized string similar to Constructor arguments cannot be passed for interface mocks.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ConstructorNotFound"> + <summary> + Looks up a localized string similar to A matching constructor for the given arguments was not found on the mocked type.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.FieldsNotSupported"> + <summary> + Looks up a localized string similar to Expression {0} involves a field access, which is not supported. Use properties instead.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.InvalidMockClass"> + <summary> + Looks up a localized string similar to Type to mock must be an interface or an abstract or non-sealed class. . + </summary> + </member> + <member name="P:Moq.Properties.Resources.InvalidMockGetType"> + <summary> + Looks up a localized string similar to Cannot retrieve a mock with the given object type {0} as it's not the main type of the mock or any of its additional interfaces. + Please cast the argument to one of the supported types: {1}. + Remember that there's no generics covariance in the CLR, so your object must be one of these types in order for the call to succeed.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.MemberMissing"> + <summary> + Looks up a localized string similar to Member {0}.{1} does not exist.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.MethodIsPublic"> + <summary> + Looks up a localized string similar to Method {0}.{1} is public. Use strong-typed Expect overload instead: + mock.Setup(x => x.{1}()); + . + </summary> + </member> + <member name="P:Moq.Properties.Resources.MockExceptionMessage"> + <summary> + Looks up a localized string similar to {0} invocation failed with mock behavior {1}. + {2}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.MoreThanNCalls"> + <summary> + Looks up a localized string similar to Expected only {0} calls to {1}.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.MoreThanOneCall"> + <summary> + Looks up a localized string similar to Expected only one call to {0}.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsAtLeast"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed on the mock less than {2} times: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsAtLeastOnce"> + <summary> + Looks up a localized string similar to {0} + Invocation was not performed on the mock: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsAtMost"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed on the mock more than {3} times: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsAtMostOnce"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed on the mock more than once: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsBetweenExclusive"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed on the mock less or equal than {2} times or more or equal than {3} times: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsBetweenInclusive"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed on the mock less than {2} times or more than {3} times: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsExactly"> + <summary> + Looks up a localized string similar to {0} + Invocation was not performed on the mock {2} times: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsNever"> + <summary> + Looks up a localized string similar to {0} + Invocation should not have been performed on the mock: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoMatchingCallsOnce"> + <summary> + Looks up a localized string similar to {0} + Invocation was performed more than once on the mock: {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.NoSetup"> + <summary> + Looks up a localized string similar to All invocations on the mock must have a corresponding setup.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ObjectInstanceNotMock"> + <summary> + Looks up a localized string similar to Object instance was not created by Moq.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.PropertyMissing"> + <summary> + Looks up a localized string similar to Property {0}.{1} does not exist.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.PropertyNotReadable"> + <summary> + Looks up a localized string similar to Property {0}.{1} is write-only.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.PropertyNotWritable"> + <summary> + Looks up a localized string similar to Property {0}.{1} is read-only.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.RaisedUnassociatedEvent"> + <summary> + Looks up a localized string similar to Cannot raise a mocked event unless it has been associated (attached) to a concrete event in a mocked object.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.ReturnValueRequired"> + <summary> + Looks up a localized string similar to Invocation needs to return a value and therefore must have a corresponding setup that provides it.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupLambda"> + <summary> + Looks up a localized string similar to A lambda expression is expected as the argument to It.Is<T>.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupNever"> + <summary> + Looks up a localized string similar to Invocation {0} should not have been made.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupNotMethod"> + <summary> + Looks up a localized string similar to Expression is not a method invocation: {0}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupNotProperty"> + <summary> + Looks up a localized string similar to Expression is not a property access: {0}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupNotSetter"> + <summary> + Looks up a localized string similar to Expression is not a property setter invocation.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.SetupOnNonOverridableMember"> + <summary> + Looks up a localized string similar to Invalid setup on a non-overridable member: + {0}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.TypeNotImplementInterface"> + <summary> + Looks up a localized string similar to Type {0} does not implement required interface {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.TypeNotInheritFromType"> + <summary> + Looks up a localized string similar to Type {0} does not from required type {1}. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnexpectedPublicProperty"> + <summary> + Looks up a localized string similar to To specify a setup for public property {0}.{1}, use the typed overloads, such as: + mock.Setup(x => x.{1}).Returns(value); + mock.SetupGet(x => x.{1}).Returns(value); //equivalent to previous one + mock.SetupSet(x => x.{1}).Callback(callbackDelegate); + . + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedExpression"> + <summary> + Looks up a localized string similar to Expression {0} is not supported.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedIntermediateExpression"> + <summary> + Looks up a localized string similar to Only property accesses are supported in intermediate invocations on a setup. Unsupported expression {0}.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedIntermediateType"> + <summary> + Looks up a localized string similar to Expression contains intermediate property access {0}.{1} which is of type {2} and cannot be mocked. Unsupported expression {3}.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedMatcherParamsForSetter"> + <summary> + Looks up a localized string similar to Setter expression cannot use argument matchers that receive parameters.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedMember"> + <summary> + Looks up a localized string similar to Member {0} is not supported for protected mocking.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedNonStaticMatcherForSetter"> + <summary> + Looks up a localized string similar to Setter expression can only use static custom matchers.. + </summary> + </member> + <member name="P:Moq.Properties.Resources.UnsupportedProtectedProperty"> + <summary> + Looks up a localized string similar to To specify a setup for protected property {0}.{1}, use: + mock.Setup<{2}>(x => x.{1}).Returns(value); + mock.SetupGet(x => x.{1}).Returns(value); //equivalent to previous one + mock.SetupSet(x => x.{1}).Callback(callbackDelegate);. + </summary> + </member> + <member name="P:Moq.Properties.Resources.VerficationFailed"> + <summary> + Looks up a localized string similar to The following setups were not matched: + {0}. + </summary> + </member> + <member name="T:Moq.Protected.IProtectedMock`1"> + <summary> + Allows setups to be specified for protected members by using their + name as a string, rather than strong-typing them which is not possible + due to their visibility. + </summary> + </member> + <member name="M:Moq.Protected.IProtectedMock`1.Setup(System.String,System.Object[])"> + <summary> + Specifies a setup for a void method invocation with the given + <paramref name="voidMethodName"/>, optionally specifying + arguments for the method call. + </summary> + <param name="voidMethodName">Name of the void method to be invoke.</param> + <param name="args">Optional arguments for the invocation. If argument matchers are used, + remember to use <see cref="T:Moq.Protected.ItExpr"/> rather than <see cref="T:Moq.It"/>.</param> + </member> + <member name="M:Moq.Protected.IProtectedMock`1.Setup``1(System.String,System.Object[])"> + <summary> + Specifies a setup for an invocation on a property or a non void method with the given + <paramref name="methodOrPropertyName"/>, optionally specifying + arguments for the method call. + </summary> + <param name="methodOrPropertyName">Name of the method or property to be invoke.</param> + <param name="args">Optional arguments for the invocation. If argument matchers are used, + remember to use <see cref="T:Moq.Protected.ItExpr"/> rather than <see cref="T:Moq.It"/>.</param> + <typeparam name="TResult">Return type of the method or property.</typeparam> + </member> + <member name="M:Moq.Protected.IProtectedMock`1.SetupGet``1(System.String)"> + <summary> + Specifies a setup for an invocation on a property getter with the given + <paramref name="propertyName"/>. + </summary> + <param name="propertyName">Name of the property.</param> + <typeparam name="TProperty">Type of the property.</typeparam> + </member> + <member name="M:Moq.Protected.IProtectedMock`1.SetupSet``1(System.String)"> + <summary> + Specifies a setup for an invocation on a property setter with the given + <paramref name="propertyName"/>. + </summary> + <param name="propertyName">Name of the property.</param> + <typeparam name="TProperty">Type of the property.</typeparam> + </member> + <member name="T:Moq.Protected.ItExpr"> + <summary> + Allows the specification of a matching condition for an + argument in a protected member setup, rather than a specific + argument value. "ItExpr" refers to the argument being matched. + </summary> + <remarks> + <para>Use this variant of argument matching instead of + <see cref="T:Moq.It"/> for protected setups.</para> + This class allows the setup to match a method invocation + with an arbitrary value, with a value in a specified range, or + even one that matches a given predicate, or null. + </remarks> + </member> + <member name="M:Moq.Protected.ItExpr.IsNull``1"> + <summary> + Matches a null value of the given <paramref name="TValue"/> type. + </summary> + <remarks> + Required for protected mocks as the null value cannot be used + directly as it prevents proper method overload selection. + </remarks> + <example> + <code> + // Throws an exception for a call to Remove with a null string value. + mock.Protected() + .Setup("Remove", ItExpr.IsNull<string>()) + .Throws(new InvalidOperationException()); + </code> + </example> + <typeparam name="TValue">Type of the value.</typeparam> + </member> + <member name="M:Moq.Protected.ItExpr.IsAny``1"> + <summary> + Matches any value of the given <paramref name="TValue"/> type. + </summary> + <remarks> + Typically used when the actual argument value for a method + call is not relevant. + </remarks> + <example> + <code> + // Throws an exception for a call to Remove with any string value. + mock.Protected() + .Setup("Remove", ItExpr.IsAny<string>()) + .Throws(new InvalidOperationException()); + </code> + </example> + <typeparam name="TValue">Type of the value.</typeparam> + </member> + <member name="M:Moq.Protected.ItExpr.Is``1(System.Linq.Expressions.Expression{System.Predicate{``0}})"> + <summary> + Matches any value that satisfies the given predicate. + </summary> + <typeparam name="TValue">Type of the argument to check.</typeparam> + <param name="match">The predicate used to match the method argument.</param> + <remarks> + Allows the specification of a predicate to perform matching + of method call arguments. + </remarks> + <example> + This example shows how to return the value <c>1</c> whenever the argument to the + <c>Do</c> method is an even number. + <code> + mock.Protected() + .Setup("Do", ItExpr.Is<int>(i => i % 2 == 0)) + .Returns(1); + </code> + This example shows how to throw an exception if the argument to the + method is a negative number: + <code> + mock.Protected() + .Setup("GetUser", ItExpr.Is<int>(i => i < 0)) + .Throws(new ArgumentException()); + </code> + </example> + </member> + <member name="M:Moq.Protected.ItExpr.IsInRange``1(``0,``0,Moq.Range)"> + <summary> + Matches any value that is in the range specified. + </summary> + <typeparam name="TValue">Type of the argument to check.</typeparam> + <param name="from">The lower bound of the range.</param> + <param name="to">The upper bound of the range.</param> + <param name="rangeKind">The kind of range. See <see cref="T:Moq.Range"/>.</param> + <example> + The following example shows how to expect a method call + with an integer argument within the 0..100 range. + <code> + mock.Protected() + .Setup("HasInventory", + ItExpr.IsAny<string>(), + ItExpr.IsInRange(0, 100, Range.Inclusive)) + .Returns(false); + </code> + </example> + </member> + <member name="M:Moq.Protected.ItExpr.IsRegex(System.String)"> + <summary> + Matches a string argument if it matches the given regular expression pattern. + </summary> + <param name="regex">The pattern to use to match the string argument value.</param> + <example> + The following example shows how to expect a call to a method where the + string argument matches the given regular expression: + <code> + mock.Protected() + .Setup("Check", ItExpr.IsRegex("[a-z]+")) + .Returns(1); + </code> + </example> + </member> + <member name="M:Moq.Protected.ItExpr.IsRegex(System.String,System.Text.RegularExpressions.RegexOptions)"> + <summary> + Matches a string argument if it matches the given regular expression pattern. + </summary> + <param name="regex">The pattern to use to match the string argument value.</param> + <param name="options">The options used to interpret the pattern.</param> + <example> + The following example shows how to expect a call to a method where the + string argument matches the given regular expression, in a case insensitive way: + <code> + mock.Protected() + .Setup("Check", ItExpr.IsRegex("[a-z]+", RegexOptions.IgnoreCase)) + .Returns(1); + </code> + </example> + </member> + <member name="T:Moq.Protected.ProtectedExtension"> + <summary> + Enables the <c>Protected()</c> method on <see cref="T:Moq.Mock`1"/>, + allowing setups to be set for protected members by using their + name as a string, rather than strong-typing them which is not possible + due to their visibility. + </summary> + </member> + <member name="M:Moq.Protected.ProtectedExtension.Protected``1(Moq.Mock{``0})"> + <summary> + Enable protected setups for the mock. + </summary> + <typeparam name="T">Mocked object type. Typically omitted as it can be inferred from the mock instance.</typeparam> + <param name="mock">The mock to set the protected setups on.</param> + </member> + <member name="T:ThisAssembly"> + <group name="overview" title="Overview" order="0" /> + <group name="setups" title="Specifying setups" order="1" /> + <group name="returns" title="Returning values from members" order="2" /> + <group name="verification" title="Verifying setups" order="3" /> + <group name="advanced" title="Advanced scenarios" order="99" /> + <group name="factory" title="Using MockFactory for consistency across mocks" order="4" /> + </member> + <member name="T:Moq.Range"> + <summary> + Kind of range to use in a filter specified through + <see cref="M:Moq.It.IsInRange``1(``0,``0,Moq.Range)"/>. + </summary> + </member> + <member name="F:Moq.Range.Inclusive"> + <summary> + The range includes the <c>to</c> and + <c>from</c> values. + </summary> + </member> + <member name="F:Moq.Range.Exclusive"> + <summary> + The range does not include the <c>to</c> and + <c>from</c> values. + </summary> + </member> + <member name="T:Moq.DefaultValue"> + <summary> + Determines the way default values are generated + calculated for loose mocks. + </summary> + </member> + <member name="F:Moq.DefaultValue.Empty"> + <summary> + Default behavior, which generates empty values for + value types (i.e. default(int)), empty array and + enumerables, and nulls for all other reference types. + </summary> + </member> + <member name="F:Moq.DefaultValue.Mock"> + <summary> + Whenever the default value generated by <see cref="F:Moq.DefaultValue.Empty"/> + is null, replaces this value with a mock (if the type + can be mocked). + </summary> + <remarks> + For sealed classes, a null value will be generated. + </remarks> + </member> + <member name="T:Moq.Match"> + <summary> + Allows creation custom value matchers that can be used on setups and verification, + completely replacing the built-in <see cref="T:Moq.It"/> class with your own argument + matching rules. + </summary> + </member> + <member name="M:Moq.Match.Matcher``1"> + <devdoc> + Provided for the sole purpose of rendering the delegate passed to the + matcher constructor if no friendly render lambda is provided. + </devdoc> + </member> + <member name="T:Moq.Match`1"> + <summary> + Allows creation custom value matchers that can be used on setups and verification, + completely replacing the built-in <see cref="T:Moq.It"/> class with your own argument + matching rules. + </summary><typeparam name="T">Type of the value to match.</typeparam><remarks> + The argument matching is used to determine whether a concrete + invocation in the mock matches a given setup. This + matching mechanism is fully extensible. + </remarks><example> + Creating a custom matcher is straightforward. You just need to create a method + that returns a value from a call to <see cref="M:Moq.Match`1.Create(System.Predicate{`0})"/> with + your matching condition and optional friendly render expression: + <code> + public Order IsBigOrder() + { + return Match<Order>.Create( + o => o.GrandTotal >= 5000, + /* a friendly expression to render on failures */ + () => IsBigOrder()); + } + </code> + This method can be used in any mock setup invocation: + <code> + mock.Setup(m => m.Submit(IsBigOrder()).Throws<UnauthorizedAccessException>(); + </code> + At runtime, Moq knows that the return value was a matcher and + evaluates your predicate with the actual value passed into your predicate. + <para> + Another example might be a case where you want to match a lists of orders + that contains a particular one. You might create matcher like the following: + </para> + <code> + public static class Orders + { + public static IEnumerable<Order> Contains(Order order) + { + return Match<IEnumerable<Order>>.Create(orders => orders.Contains(order)); + } + } + </code> + Now we can invoke this static method instead of an argument in an + invocation: + <code> + var order = new Order { ... }; + var mock = new Mock<IRepository<Order>>(); + + mock.Setup(x => x.Save(Orders.Contains(order))) + .Throws<ArgumentException>(); + </code> + </example> + </member> + <member name="M:Moq.Match`1.Create(System.Predicate{`0})"> + <summary> + Initializes the match with the condition that + will be checked in order to match invocation + values. + </summary><param name="condition">The condition to match against actual values.</param><remarks> + <seealso cref="T:Moq.Match`1"/> + </remarks> + </member> + <member name="M:Moq.Match`1.Create(System.Predicate{`0},System.Linq.Expressions.Expression{System.Func{`0}})"> + <!-- No matching elements were found for the following include tag --><include file="Match.xdoc" path="docs/doc[@for="Match{T}.Create(condition,renderExpression"]/*"/> + </member> + <member name="M:Moq.Match`1.Convert"> + <!-- No matching elements were found for the following include tag --><include file="Match.xdoc" path="docs/doc[@for="Match{T}.Convert"]/*"/> + </member> + <member name="M:Moq.Match`1.SetLastMatch``1(Moq.Match{``0})"> + <devdoc> + This method is used to set an expression as the last matcher invoked, + which is used in the SetupSet to allow matchers in the prop = value + delegate expression. This delegate is executed in "fluent" mode in + order to capture the value being set, and construct the corresponding + methodcall. + This is also used in the MatcherFactory for each argument expression. + This method ensures that when we execute the delegate, we + also track the matcher that was invoked, so that when we create the + methodcall we build the expression using it, rather than the null/default + value returned from the actual invocation. + </devdoc> + </member> + <member name="T:Moq.Mock`1"> + <summary> + Provides a mock implementation of <typeparamref name="T"/>. + </summary><remarks> + Any interface type can be used for mocking, but for classes, only abstract and virtual members can be mocked. + <para> + The behavior of the mock with regards to the setups and the actual calls is determined + by the optional <see cref="T:Moq.MockBehavior"/> that can be passed to the <see cref="M:Moq.Mock`1.#ctor(Moq.MockBehavior)"/> + constructor. + </para> + </remarks><typeparam name="T">Type to mock, which can be an interface or a class.</typeparam><example group="overview" order="0"> + The following example shows establishing setups with specific values + for method invocations: + <code> + // Arrange + var order = new Order(TALISKER, 50); + var mock = new Mock<IWarehouse>(); + + mock.Setup(x => x.HasInventory(TALISKER, 50)).Returns(true); + + // Act + order.Fill(mock.Object); + + // Assert + Assert.True(order.IsFilled); + </code> + The following example shows how to use the <see cref="T:Moq.It"/> class + to specify conditions for arguments instead of specific values: + <code> + // Arrange + var order = new Order(TALISKER, 50); + var mock = new Mock<IWarehouse>(); + + // shows how to expect a value within a range + mock.Setup(x => x.HasInventory( + It.IsAny<string>(), + It.IsInRange(0, 100, Range.Inclusive))) + .Returns(false); + + // shows how to throw for unexpected calls. + mock.Setup(x => x.Remove( + It.IsAny<string>(), + It.IsAny<int>())) + .Throws(new InvalidOperationException()); + + // Act + order.Fill(mock.Object); + + // Assert + Assert.False(order.IsFilled); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.#ctor(System.Boolean)"> + <summary> + Ctor invoked by AsTInterface exclusively. + </summary> + </member> + <member name="M:Moq.Mock`1.#ctor"> + <summary> + Initializes an instance of the mock with <see cref="F:Moq.MockBehavior.Default">default behavior</see>. + </summary><example> + <code>var mock = new Mock<IFormatProvider>();</code> + </example> + </member> + <member name="M:Moq.Mock`1.#ctor(System.Object[])"> + <summary> + Initializes an instance of the mock with <see cref="F:Moq.MockBehavior.Default">default behavior</see> and with + the given constructor arguments for the class. (Only valid when <typeparamref name="T"/> is a class) + </summary><remarks> + The mock will try to find the best match constructor given the constructor arguments, and invoke that + to initialize the instance. This applies only for classes, not interfaces. + </remarks><example> + <code>var mock = new Mock<MyProvider>(someArgument, 25);</code> + </example><param name="args">Optional constructor arguments if the mocked type is a class.</param> + </member> + <member name="M:Moq.Mock`1.#ctor(Moq.MockBehavior)"> + <summary> + Initializes an instance of the mock with the specified <see cref="T:Moq.MockBehavior">behavior</see>. + </summary><example> + <code>var mock = new Mock<IFormatProvider>(MockBehavior.Relaxed);</code> + </example><param name="behavior">Behavior of the mock.</param> + </member> + <member name="M:Moq.Mock`1.#ctor(Moq.MockBehavior,System.Object[])"> + <summary> + Initializes an instance of the mock with a specific <see cref="T:Moq.MockBehavior">behavior</see> with + the given constructor arguments for the class. + </summary><remarks> + The mock will try to find the best match constructor given the constructor arguments, and invoke that + to initialize the instance. This applies only to classes, not interfaces. + </remarks><example> + <code>var mock = new Mock<MyProvider>(someArgument, 25);</code> + </example><param name="behavior">Behavior of the mock.</param><param name="args">Optional constructor arguments if the mocked type is a class.</param> + </member> + <member name="M:Moq.Mock`1.GetObject"> + <summary> + Returns the mocked object value. + </summary> + </member> + <member name="M:Moq.Mock`1.Setup(System.Linq.Expressions.Expression{System.Action{`0}})"> + <summary> + Specifies a setup on the mocked type for a call to + to a void method. + </summary><remarks> + If more than one setup is specified for the same method or property, + the latest one wins and is the one that will be executed. + </remarks><param name="expression">Lambda expression that specifies the expected method invocation.</param><example group="setups"> + <code> + var mock = new Mock<IProcessor>(); + mock.Setup(x => x.Execute("ping")); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.Setup``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Specifies a setup on the mocked type for a call to + to a value returning method. + </summary><typeparam name="TResult">Type of the return value. Typically omitted as it can be inferred from the expression.</typeparam><remarks> + If more than one setup is specified for the same method or property, + the latest one wins and is the one that will be executed. + </remarks><param name="expression">Lambda expression that specifies the method invocation.</param><example group="setups"> + <code> + mock.Setup(x => x.HasInventory("Talisker", 50)).Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Specifies a setup on the mocked type for a call to + to a property getter. + </summary><remarks> + If more than one setup is set for the same property getter, + the latest one wins and is the one that will be executed. + </remarks><typeparam name="TProperty">Type of the property. Typically omitted as it can be inferred from the expression.</typeparam><param name="expression">Lambda expression that specifies the property getter.</param><example group="setups"> + <code> + mock.SetupGet(x => x.Suspended) + .Returns(true); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupSet``1(System.Action{`0})"> + <summary> + Specifies a setup on the mocked type for a call to + to a property setter. + </summary><remarks> + If more than one setup is set for the same property setter, + the latest one wins and is the one that will be executed. + <para> + This overloads allows the use of a callback already + typed for the property type. + </para> + </remarks><typeparam name="TProperty">Type of the property. Typically omitted as it can be inferred from the expression.</typeparam><param name="setterExpression">Lambda expression that sets a property to a value.</param><example group="setups"> + <code> + mock.SetupSet(x => x.Suspended = true); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupSet(System.Action{`0})"> + <summary> + Specifies a setup on the mocked type for a call to + to a property setter. + </summary><remarks> + If more than one setup is set for the same property setter, + the latest one wins and is the one that will be executed. + </remarks><param name="setterExpression">Lambda expression that sets a property to a value.</param><example group="setups"> + <code> + mock.SetupSet(x => x.Suspended = true); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupProperty``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Specifies that the given property should have "property behavior", + meaning that setting its value will cause it to be saved and + later returned when the property is requested. (this is also + known as "stubbing"). + </summary><typeparam name="TProperty"> + Type of the property, inferred from the property + expression (does not need to be specified). + </typeparam><param name="property">Property expression to stub.</param><example> + If you have an interface with an int property <c>Value</c>, you might + stub it using the following straightforward call: + <code> + var mock = new Mock<IHaveValue>(); + mock.Stub(v => v.Value); + </code> + After the <c>Stub</c> call has been issued, setting and + retrieving the object value will behave as expected: + <code> + IHaveValue v = mock.Object; + + v.Value = 5; + Assert.Equal(5, v.Value); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupProperty``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},``0)"> + <summary> + Specifies that the given property should have "property behavior", + meaning that setting its value will cause it to be saved and + later returned when the property is requested. This overload + allows setting the initial value for the property. (this is also + known as "stubbing"). + </summary><typeparam name="TProperty"> + Type of the property, inferred from the property + expression (does not need to be specified). + </typeparam><param name="property">Property expression to stub.</param><param name="initialValue">Initial value for the property.</param><example> + If you have an interface with an int property <c>Value</c>, you might + stub it using the following straightforward call: + <code> + var mock = new Mock<IHaveValue>(); + mock.SetupProperty(v => v.Value, 5); + </code> + After the <c>SetupProperty</c> call has been issued, setting and + retrieving the object value will behave as expected: + <code> + IHaveValue v = mock.Object; + // Initial value was stored + Assert.Equal(5, v.Value); + + // New value set which changes the initial value + v.Value = 6; + Assert.Equal(6, v.Value); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.SetupAllProperties"> + <summary> + Specifies that the all properties on the mock should have "property behavior", + meaning that setting its value will cause it to be saved and + later returned when the property is requested. (this is also + known as "stubbing"). The default value for each property will be the + one generated as specified by the <see cref="P:Moq.Mock.DefaultValue"/> property for the mock. + </summary><remarks> + If the mock <see cref="P:Moq.Mock.DefaultValue"/> is set to <see cref="F:Moq.DefaultValue.Mock"/>, + the mocked default values will also get all properties setup recursively. + </remarks> + </member> + <member name="M:Moq.Mock`1.Verify(System.Linq.Expressions.Expression{System.Action{`0}})"> + <summary> + Verifies that a specific invocation matching the given expression was performed on the mock. Use + in conjuntion with the default <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><example group="verification"> + This example assumes that the mock has been used, and later we want to verify that a given + invocation with specific parameters was performed: + <code> + var mock = new Mock<IProcessor>(); + // exercise mock + //... + // Will throw if the test code didn't call Execute with a "ping" string argument. + mock.Verify(proc => proc.Execute("ping")); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param> + </member> + <member name="M:Moq.Mock`1.Verify(System.Linq.Expressions.Expression{System.Action{`0}},Moq.Times)"> + <summary> + Verifies that a specific invocation matching the given expression was performed on the mock. Use + in conjuntion with the default <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="expression">Expression to verify.</param><param name="times">The number of times a method is allowed to be called.</param> + </member> + <member name="M:Moq.Mock`1.Verify(System.Linq.Expressions.Expression{System.Action{`0}},System.String)"> + <summary> + Verifies that a specific invocation matching the given expression was performed on the mock, + specifying a failure error message. Use in conjuntion with the default + <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><example group="verification"> + This example assumes that the mock has been used, and later we want to verify that a given + invocation with specific parameters was performed: + <code> + var mock = new Mock<IProcessor>(); + // exercise mock + //... + // Will throw if the test code didn't call Execute with a "ping" string argument. + mock.Verify(proc => proc.Execute("ping")); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param> + </member> + <member name="M:Moq.Mock`1.Verify(System.Linq.Expressions.Expression{System.Action{`0}},Moq.Times,System.String)"> + <summary> + Verifies that a specific invocation matching the given expression was performed on the mock, + specifying a failure error message. Use in conjuntion with the default + <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="expression">Expression to verify.</param><param name="times">The number of times a method is allowed to be called.</param><param name="failMessage">Message to show if verification fails.</param> + </member> + <member name="M:Moq.Mock`1.Verify``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Verifies that a specific invocation matching the given expression was performed on the mock. Use + in conjuntion with the default <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><example group="verification"> + This example assumes that the mock has been used, and later we want to verify that a given + invocation with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't call HasInventory. + mock.Verify(warehouse => warehouse.HasInventory(TALISKER, 50)); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param><typeparam name="TResult">Type of return value from the expression.</typeparam> + </member> + <member name="M:Moq.Mock`1.Verify``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},Moq.Times)"> + <summary> + Verifies that a specific invocation matching the given + expression was performed on the mock. Use in conjuntion + with the default <see cref="F:Moq.MockBehavior.Loose"/>. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="expression">Expression to verify.</param><param name="times">The number of times a method is allowed to be called.</param><typeparam name="TResult">Type of return value from the expression.</typeparam> + </member> + <member name="M:Moq.Mock`1.Verify``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},System.String)"> + <summary> + Verifies that a specific invocation matching the given + expression was performed on the mock, specifying a failure + error message. + </summary><example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given invocation + with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't call HasInventory. + mock.Verify(warehouse => warehouse.HasInventory(TALISKER, 50), "When filling orders, inventory has to be checked"); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param><typeparam name="TResult">Type of return value from the expression.</typeparam> + </member> + <member name="M:Moq.Mock`1.Verify``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},Moq.Times,System.String)"> + <summary> + Verifies that a specific invocation matching the given + expression was performed on the mock, specifying a failure + error message. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="expression">Expression to verify.</param><param name="times">The number of times a method is allowed to be called.</param><param name="failMessage">Message to show if verification fails.</param><typeparam name="TResult">Type of return value from the expression.</typeparam> + </member> + <member name="M:Moq.Mock`1.VerifyGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Verifies that a property was read on the mock. + </summary><example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given property + was retrieved from it: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't retrieve the IsClosed property. + mock.VerifyGet(warehouse => warehouse.IsClosed); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param><typeparam name="TProperty"> + Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type. + </typeparam> + </member> + <member name="M:Moq.Mock`1.VerifyGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},Moq.Times)"> + <summary> + Verifies that a property was read on the mock. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="times">The number of times a method is allowed to be called.</param><param name="expression">Expression to verify.</param><typeparam name="TProperty"> + Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type. + </typeparam> + </member> + <member name="M:Moq.Mock`1.VerifyGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},System.String)"> + <summary> + Verifies that a property was read on the mock, specifying a failure + error message. + </summary><example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given property + was retrieved from it: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't retrieve the IsClosed property. + mock.VerifyGet(warehouse => warehouse.IsClosed); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="expression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param><typeparam name="TProperty"> + Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type. + </typeparam> + </member> + <member name="M:Moq.Mock`1.VerifyGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},Moq.Times,System.String)"> + <summary> + Verifies that a property was read on the mock, specifying a failure + error message. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="times">The number of times a method is allowed to be called.</param><param name="expression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param><typeparam name="TProperty"> + Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type. + </typeparam> + </member> + <member name="M:Moq.Mock`1.VerifySet(System.Action{`0})"> + <summary> + Verifies that a property was set on the mock. + </summary><example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given property + was set on it: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed = true); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="setterExpression">Expression to verify.</param> + </member> + <member name="M:Moq.Mock`1.VerifySet(System.Action{`0},Moq.Times)"> + <summary> + Verifies that a property was set on the mock. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="times">The number of times a method is allowed to be called.</param><param name="setterExpression">Expression to verify.</param> + </member> + <member name="M:Moq.Mock`1.VerifySet(System.Action{`0},System.String)"> + <summary> + Verifies that a property was set on the mock, specifying + a failure message. + </summary><example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given property + was set on it: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed = true, "Warehouse should always be closed after the action"); + </code> + </example><exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception><param name="setterExpression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param> + </member> + <member name="M:Moq.Mock`1.VerifySet(System.Action{`0},Moq.Times,System.String)"> + <summary> + Verifies that a property was set on the mock, specifying + a failure message. + </summary><exception cref="T:Moq.MockException"> + The invocation was not call the times specified by + <paramref name="times"/>. + </exception><param name="times">The number of times a method is allowed to be called.</param><param name="setterExpression">Expression to verify.</param><param name="failMessage">Message to show if verification fails.</param> + </member> + <member name="M:Moq.Mock`1.As``1"> + <summary> + Adds an interface implementation to the mock, + allowing setups to be specified for it. + </summary><remarks> + This method can only be called before the first use + of the mock <see cref="P:Moq.Mock`1.Object"/> property, at which + point the runtime type has already been generated + and no more interfaces can be added to it. + <para> + Also, <typeparamref name="TInterface"/> must be an + interface and not a class, which must be specified + when creating the mock instead. + </para> + </remarks><exception cref="T:System.InvalidOperationException"> + The mock type + has already been generated by accessing the <see cref="P:Moq.Mock`1.Object"/> property. + </exception><exception cref="T:System.ArgumentException"> + The <typeparamref name="TInterface"/> specified + is not an interface. + </exception><example> + The following example creates a mock for the main interface + and later adds <see cref="T:System.IDisposable"/> to it to verify + it's called by the consumer code: + <code> + var mock = new Mock<IProcessor>(); + mock.Setup(x => x.Execute("ping")); + + // add IDisposable interface + var disposable = mock.As<IDisposable>(); + disposable.Setup(d => d.Dispose()).Verifiable(); + </code> + </example><typeparam name="TInterface">Type of interface to cast the mock to.</typeparam> + </member> + <member name="M:Moq.Mock`1.Raise(System.Action{`0},System.EventArgs)"> + <summary> + Raises the event referenced in <paramref name="eventExpression"/> using + the given <paramref name="sender"/> and <paramref name="args"/> arguments. + </summary><exception cref="T:System.ArgumentException"> + The <paramref name="args"/> argument is + invalid for the target event invocation, or the <paramref name="eventExpression"/> is + not an event attach or detach expression. + </exception><example> + The following example shows how to raise a <see cref="E:System.ComponentModel.INotifyPropertyChanged.PropertyChanged"/> event: + <code> + var mock = new Mock<IViewModel>(); + + mock.Raise(x => x.PropertyChanged -= null, new PropertyChangedEventArgs("Name")); + </code> + </example><example> + This example shows how to invoke an event with a custom event arguments + class in a view that will cause its corresponding presenter to + react by changing its state: + <code> + var mockView = new Mock<IOrdersView>(); + var presenter = new OrdersPresenter(mockView.Object); + + // Check that the presenter has no selection by default + Assert.Null(presenter.SelectedOrder); + + // Raise the event with a specific arguments data + mockView.Raise(v => v.SelectionChanged += null, new OrderEventArgs { Order = new Order("moq", 500) }); + + // Now the presenter reacted to the event, and we have a selected order + Assert.NotNull(presenter.SelectedOrder); + Assert.Equal("moq", presenter.SelectedOrder.ProductName); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.Raise(System.Action{`0},System.Object[])"> + <summary> + Raises the event referenced in <paramref name="eventExpression"/> using + the given <paramref name="sender"/> and <paramref name="args"/> arguments + for a non-EventHandler typed event. + </summary><exception cref="T:System.ArgumentException"> + The <paramref name="args"/> arguments are + invalid for the target event invocation, or the <paramref name="eventExpression"/> is + not an event attach or detach expression. + </exception><example> + The following example shows how to raise a custom event that does not adhere to + the standard <c>EventHandler</c>: + <code> + var mock = new Mock<IViewModel>(); + + mock.Raise(x => x.MyEvent -= null, "Name", bool, 25); + </code> + </example> + </member> + <member name="M:Moq.Mock`1.Expect(System.Linq.Expressions.Expression{System.Action{`0}})"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.Mock`1.Expect``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.Mock`1.ExpectGet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.Mock`1.ExpectSet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.Mock`1.ExpectSet``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},``0)"> + <summary> + Obsolete. + </summary> + </member> + <member name="P:Moq.Mock`1.Object"> + <summary> + Exposes the mocked object instance. + </summary> + </member> + <member name="T:Moq.MockLegacyExtensions"> + <summary> + Provides legacy API members as extensions so that + existing code continues to compile, but new code + doesn't see then. + </summary> + </member> + <member name="M:Moq.MockLegacyExtensions.SetupSet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},``1)"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.MockLegacyExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},``1)"> + <summary> + Obsolete. + </summary> + </member> + <member name="M:Moq.MockLegacyExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},``1,System.String)"> + <summary> + Obsolete. + </summary> + </member> + <member name="T:Moq.FluentMockContext"> + <summary> + Tracks the current mock and interception context. + </summary> + </member> + <member name="P:Moq.FluentMockContext.IsActive"> + <summary> + Having an active fluent mock context means that the invocation + is being performed in "trial" mode, just to gather the + target method and arguments that need to be matched later + when the actual invocation is made. + </summary> + </member> + <member name="T:Moq.MockDefaultValueProvider"> + <summary> + A <see cref="T:Moq.IDefaultValueProvider"/> that returns an empty default value + for non-mockeable types, and mocks for all other types (interfaces and + non-sealed classes) that can be mocked. + </summary> + </member> + <member name="T:Moq.MockedEvent`1"> + <summary> + Provides a typed <see cref="T:Moq.MockedEvent"/> for a + specific type of <see cref="T:System.EventArgs"/>. + </summary> + <typeparam name="TEventArgs">The type of event arguments required by the event.</typeparam> + <remarks> + The mocked event can either be a <see cref="T:System.EventHandler`1"/> or custom + event handler which follows .NET practice of providing <c>object sender, EventArgs args</c> + kind of signature. + </remarks> + </member> + <member name="M:Moq.MockedEvent`1.Raise(`0)"> + <summary> + Raises the associated event with the given + event argument data. + </summary> + <param name="args">Data to pass to the event.</param> + </member> + <member name="M:Moq.MockedEvent`1.op_Implicit(Moq.MockedEvent{`0})~System.EventHandler{`0}"> + <summary> + Provides support for attaching a <see cref="T:Moq.MockedEvent`1"/> to + a generic <see cref="T:System.EventHandler`1"/> event. + </summary> + <param name="mockEvent">Event to convert.</param> + </member> + <member name="M:Moq.MockedEvent`1.Handle(System.Object,`0)"> + <summary> + Provided solely to allow the interceptor to determine when the attached + handler is coming from this mocked event so we can assign the + corresponding EventInfo for it. + </summary> + </member> + <member name="T:Moq.MockExtensions"> + <summary> + Provides additional methods on mocks. + </summary> + <devdoc> + Provided as extension methods as they confuse the compiler + with the overloads taking Action. + </devdoc> + </member> + <member name="M:Moq.MockExtensions.SetupSet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}})"> + <summary> + Specifies a setup on the mocked type for a call to + to a property setter, regardless of its value. + </summary> + <remarks> + If more than one setup is set for the same property setter, + the latest one wins and is the one that will be executed. + </remarks> + <typeparam name="TProperty">Type of the property. Typically omitted as it can be inferred from the expression.</typeparam> + <typeparam name="T">Type of the mock.</typeparam> + <param name="mock">The target mock for the setup.</param> + <param name="expression">Lambda expression that specifies the property setter.</param> + <example group="setups"> + <code> + mock.SetupSet(x => x.Suspended); + </code> + </example> + <devdoc> + This method is not legacy, but must be on an extension method to avoid + confusing the compiler with the new Action syntax. + </devdoc> + </member> + <member name="M:Moq.MockExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}})"> + <summary> + Verifies that a property has been set on the mock, regarless of its value. + </summary> + <example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given invocation + with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed); + </code> + </example> + <exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception> + <param name="expression">Expression to verify.</param> + <param name="mock">The mock instance.</param> + <typeparam name="T">Mocked type.</typeparam> + <typeparam name="TProperty">Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type.</typeparam> + </member> + <member name="M:Moq.MockExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.String)"> + <summary> + Verifies that a property has been set on the mock, specifying a failure + error message. + </summary> + <example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given invocation + with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed); + </code> + </example> + <exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception> + <param name="expression">Expression to verify.</param> + <param name="failMessage">Message to show if verification fails.</param> + <param name="mock">The mock instance.</param> + <typeparam name="T">Mocked type.</typeparam> + <typeparam name="TProperty">Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type.</typeparam> + </member> + <member name="M:Moq.MockExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},Moq.Times)"> + <summary> + Verifies that a property has been set on the mock, regardless + of the value but only the specified number of times. + </summary> + <example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given invocation + with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed); + </code> + </example> + <exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception> + <exception cref="T:Moq.MockException">The invocation was not call the times specified by + <paramref name="times"/>.</exception> + <param name="mock">The mock instance.</param> + <typeparam name="T">Mocked type.</typeparam> + <param name="times">The number of times a method is allowed to be called.</param> + <param name="expression">Expression to verify.</param> + <typeparam name="TProperty">Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type.</typeparam> + </member> + <member name="M:Moq.MockExtensions.VerifySet``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},Moq.Times,System.String)"> + <summary> + Verifies that a property has been set on the mock, regardless + of the value but only the specified number of times, and specifying a failure + error message. + </summary> + <example group="verification"> + This example assumes that the mock has been used, + and later we want to verify that a given invocation + with specific parameters was performed: + <code> + var mock = new Mock<IWarehouse>(); + // exercise mock + //... + // Will throw if the test code didn't set the IsClosed property. + mock.VerifySet(warehouse => warehouse.IsClosed); + </code> + </example> + <exception cref="T:Moq.MockException">The invocation was not performed on the mock.</exception> + <exception cref="T:Moq.MockException">The invocation was not call the times specified by + <paramref name="times"/>.</exception> + <param name="mock">The mock instance.</param> + <typeparam name="T">Mocked type.</typeparam> + <param name="times">The number of times a method is allowed to be called.</param> + <param name="failMessage">Message to show if verification fails.</param> + <param name="expression">Expression to verify.</param> + <typeparam name="TProperty">Type of the property to verify. Typically omitted as it can + be inferred from the expression's return type.</typeparam> + </member> + <member name="T:Moq.Stub.StubExtensions"> + <summary> + Legacy Stub stuff, moved to the core API. + </summary> + </member> + <member name="M:Moq.Stub.StubExtensions.Stub``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}})"> + <summary> + Obsolete. Use <see cref="M:Moq.Mock`1.SetupProperty``1(System.Linq.Expressions.Expression{System.Func{`0,``0}})"/>. + </summary> + </member> + <member name="M:Moq.Stub.StubExtensions.Stub``2(Moq.Mock{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},``1)"> + <summary> + Obsolete. Use <see cref="M:Moq.Mock`1.SetupProperty``1(System.Linq.Expressions.Expression{System.Func{`0,``0}},``0)"/>. + </summary> + </member> + <member name="M:Moq.Stub.StubExtensions.StubAll``1(Moq.Mock{``0})"> + <summary> + Obsolete. Use <see cref="M:Moq.Mock`1.SetupAllProperties"/>. + </summary> + </member> + <member name="T:Moq.Times"> + <summary> + Defines the number of invocations allowed by a mocked method. + </summary> + </member> + <member name="M:Moq.Times.AtLeast(System.Int32)"> + <summary> + Specifies that a mocked method should be invoked <paramref name="times"/> times as minimum. + </summary> + <param name="callCount">The minimun number of times.</param> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.AtLeastOnce"> + <summary> + Specifies that a mocked method should be invoked one time as minimum. + </summary> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.AtMost(System.Int32)"> + <summary> + Specifies that a mocked method should be invoked <paramref name="times"/> time as maximun. + </summary> + <param name="callCount">The maximun number of times.</param> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.AtMostOnce"> + <summary> + Specifies that a mocked method should be invoked one time as maximun. + </summary> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.Between(System.Int32,System.Int32,Moq.Range)"> + <summary> + Specifies that a mocked method should be invoked between <paramref name="from"/> and + <paramref name="to"/> times. + </summary> + <param name="callCountFrom">The minimun number of times.</param> + <param name="callCountTo">The maximun number of times.</param> + <param name="rangeKind">The kind of range. See <see cref="T:Moq.Range"/>.</param> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.Exactly(System.Int32)"> + <summary> + Specifies that a mocked method should be invoked exactly <paramref name="times"/> times. + </summary> + <param name="callCount">The times that a method or property can be called.</param> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.Never"> + <summary> + Specifies that a mocked method should not be invoked. + </summary> + <returns>An object defining the allowed number of invocations.</returns> + </member> + <member name="M:Moq.Times.Once"> + <summary> + Specifies that a mocked method should be invoked exactly one time. + </summary> + <returns>An object defining the allowed number of invocations.</returns> + </member> + </members> +</doc> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs index e8511ae..4d3ce13 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs @@ -48,6 +48,7 @@ namespace DotNetOpenAuth.ApplicationBlock { { Applications.Finance, "http://finance.google.com/finance/feeds/" }, { Applications.Gmail, "https://mail.google.com/mail/feed/atom" }, { Applications.Health, "https://www.google.com/h9/feeds/" }, + { Applications.Maps, "http://maps.google.com/maps/feeds/" }, { Applications.OpenSocial, "http://sandbox.gmodules.com/api/" }, { Applications.PicasaWeb, "http://picasaweb.google.com/data/" }, { Applications.Spreadsheets, "http://spreadsheets.google.com/feeds/" }, @@ -139,6 +140,11 @@ namespace DotNetOpenAuth.ApplicationBlock { /// Google Analytics /// </summary> Analytics = 0x4000, + + /// <summary> + /// Google Maps + /// </summary> + Maps = 0x8000, } /// <summary> diff --git a/samples/OAuthConsumerWpf/Authorize.xaml b/samples/OAuthConsumerWpf/Authorize.xaml new file mode 100644 index 0000000..5aa2349 --- /dev/null +++ b/samples/OAuthConsumerWpf/Authorize.xaml @@ -0,0 +1,12 @@ +<Window x:Class="DotNetOpenAuth.Samples.OAuthConsumerWpf.Authorize" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + Title="Authorize" Width="300" ShowInTaskbar="False"> + <StackPanel> + <TextBlock TextWrapping="Wrap">Complete authorization at your service provider, + then enter the verification code below and click Finish:</TextBlock> + <TextBox Name="verifierBox"/> + <Button Name="finishButton" Click="finishButton_Click">Finish</Button> + <Button Name="cancelButton" Click="cancelButton_Click">Cancel</Button> + </StackPanel> +</Window> diff --git a/samples/OAuthConsumerWpf/Authorize.xaml.cs b/samples/OAuthConsumerWpf/Authorize.xaml.cs new file mode 100644 index 0000000..c28e6cc --- /dev/null +++ b/samples/OAuthConsumerWpf/Authorize.xaml.cs @@ -0,0 +1,59 @@ +namespace DotNetOpenAuth.Samples.OAuthConsumerWpf { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Data; + using System.Windows.Documents; + using System.Windows.Input; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Shapes; + using System.Xml.Linq; + using DotNetOpenAuth.ApplicationBlock; + using DotNetOpenAuth.OAuth; + + /// <summary> + /// Interaction logic for Authorize.xaml + /// </summary> + public partial class Authorize : Window { + private DesktopConsumer google; + private string requestToken; + + internal Authorize(DesktopConsumer consumer) { + InitializeComponent(); + + this.google = consumer; + Cursor original = this.Cursor; + this.Cursor = Cursors.Wait; + ThreadPool.QueueUserWorkItem(delegate(object state) { + Uri browserAuthorizationLocation = GoogleConsumer.RequestAuthorization( + this.google, + GoogleConsumer.Applications.Contacts | GoogleConsumer.Applications.Blogger, + out this.requestToken); + System.Diagnostics.Process.Start(browserAuthorizationLocation.AbsoluteUri); + this.Dispatcher.BeginInvoke(new Action(() => { + this.Cursor = original; + finishButton.IsEnabled = true; + })); + }); + } + + internal string AccessToken { get; set; } + + private void finishButton_Click(object sender, RoutedEventArgs e) { + var grantedAccess = this.google.ProcessUserAuthorization(this.requestToken, verifierBox.Text); + this.AccessToken = grantedAccess.AccessToken; + DialogResult = true; + Close(); + } + + private void cancelButton_Click(object sender, RoutedEventArgs e) { + DialogResult = false; + Close(); + } + } +} diff --git a/samples/OAuthConsumerWpf/MainWindow.xaml b/samples/OAuthConsumerWpf/MainWindow.xaml index fb036ce..6ada88a 100644 --- a/samples/OAuthConsumerWpf/MainWindow.xaml +++ b/samples/OAuthConsumerWpf/MainWindow.xaml @@ -14,10 +14,7 @@ <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> - <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="3"> - <Button Name="beginAuthorizationButton" Click="beginAuthorizationButton_Click">Start authorize</Button> - <Button Name="completeAuthorizationButton" Margin="5,0,0,0" Click="completeAuthorizationButton_Click" IsEnabled="false">Complete authorization</Button> - </StackPanel> + <Button Grid.Column="1" Grid.Row="3" Name="beginAuthorizationButton" Click="beginAuthorizationButton_Click">Authorize</Button> <TabControl Grid.ColumnSpan="2" Grid.Row="4" Name="tabControl1" Margin="0,10,0,0"> <TabItem Header="Gmail Contacts" Name="gmailContactsTab"> <Grid Name="contactsGrid"> diff --git a/samples/OAuthConsumerWpf/MainWindow.xaml.cs b/samples/OAuthConsumerWpf/MainWindow.xaml.cs index 6c1c2ba..e408d19 100644 --- a/samples/OAuthConsumerWpf/MainWindow.xaml.cs +++ b/samples/OAuthConsumerWpf/MainWindow.xaml.cs @@ -30,7 +30,6 @@ public partial class MainWindow : Window { private InMemoryTokenManager tokenManager = new InMemoryTokenManager(); private DesktopConsumer google; - private string requestToken; private string accessToken; public MainWindow() { @@ -56,40 +55,26 @@ return; } - Cursor original = this.Cursor; - this.Cursor = Cursors.Wait; - beginAuthorizationButton.IsEnabled = false; - ThreadPool.QueueUserWorkItem(delegate(object state) { - Uri browserAuthorizationLocation = GoogleConsumer.RequestAuthorization( - this.google, - GoogleConsumer.Applications.Contacts | GoogleConsumer.Applications.Blogger, - out this.requestToken); - System.Diagnostics.Process.Start(browserAuthorizationLocation.AbsoluteUri); - this.Dispatcher.BeginInvoke(new Action(() => { - this.Cursor = original; - beginAuthorizationButton.IsEnabled = true; - completeAuthorizationButton.IsEnabled = true; - postButton.IsEnabled = true; - })); - }); - } + Authorize auth = new Authorize(this.google); + bool? result = auth.ShowDialog(); + if (result.HasValue && result.Value) { + this.accessToken = auth.AccessToken; + postButton.IsEnabled = true; - private void completeAuthorizationButton_Click(object sender, RoutedEventArgs e) { - var grantedAccess = this.google.ProcessUserAuthorization(this.requestToken); - this.accessToken = grantedAccess.AccessToken; - XDocument contactsDocument = GoogleConsumer.GetContacts(this.google, grantedAccess.AccessToken); - var contacts = from entry in contactsDocument.Root.Elements(XName.Get("entry", "http://www.w3.org/2005/Atom")) - select new { Name = entry.Element(XName.Get("title", "http://www.w3.org/2005/Atom")).Value, Email = entry.Element(XName.Get("email", "http://schemas.google.com/g/2005")).Attribute("address").Value }; - contactsGrid.Children.Clear(); - foreach (var contact in contacts) { - contactsGrid.RowDefinitions.Add(new RowDefinition()); - TextBlock name = new TextBlock { Text = contact.Name }; - TextBlock email = new TextBlock { Text = contact.Email }; - Grid.SetRow(name, contactsGrid.RowDefinitions.Count - 1); - Grid.SetRow(email, contactsGrid.RowDefinitions.Count - 1); - Grid.SetColumn(email, 1); - contactsGrid.Children.Add(name); - contactsGrid.Children.Add(email); + XDocument contactsDocument = GoogleConsumer.GetContacts(this.google, this.accessToken); + var contacts = from entry in contactsDocument.Root.Elements(XName.Get("entry", "http://www.w3.org/2005/Atom")) + select new { Name = entry.Element(XName.Get("title", "http://www.w3.org/2005/Atom")).Value, Email = entry.Element(XName.Get("email", "http://schemas.google.com/g/2005")).Attribute("address").Value }; + contactsGrid.Children.Clear(); + foreach (var contact in contacts) { + contactsGrid.RowDefinitions.Add(new RowDefinition()); + TextBlock name = new TextBlock { Text = contact.Name }; + TextBlock email = new TextBlock { Text = contact.Email }; + Grid.SetRow(name, contactsGrid.RowDefinitions.Count - 1); + Grid.SetRow(email, contactsGrid.RowDefinitions.Count - 1); + Grid.SetColumn(email, 1); + contactsGrid.Children.Add(name); + contactsGrid.Children.Add(email); + } } } diff --git a/samples/OAuthConsumerWpf/OAuthConsumerWpf.csproj b/samples/OAuthConsumerWpf/OAuthConsumerWpf.csproj index 0617746..6e8e4ea 100644 --- a/samples/OAuthConsumerWpf/OAuthConsumerWpf.csproj +++ b/samples/OAuthConsumerWpf/OAuthConsumerWpf.csproj @@ -65,15 +65,28 @@ </Reference> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> - <Reference Include="WindowsBase" /> - <Reference Include="PresentationCore" /> - <Reference Include="PresentationFramework" /> + <Reference Include="UIAutomationProvider"> + <RequiredTargetFramework>3.0</RequiredTargetFramework> + </Reference> + <Reference Include="WindowsBase"> + <RequiredTargetFramework>3.0</RequiredTargetFramework> + </Reference> + <Reference Include="PresentationCore"> + <RequiredTargetFramework>3.0</RequiredTargetFramework> + </Reference> + <Reference Include="PresentationFramework"> + <RequiredTargetFramework>3.0</RequiredTargetFramework> + </Reference> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> + <Page Include="Authorize.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -88,6 +101,9 @@ </Compile> </ItemGroup> <ItemGroup> + <Compile Include="Authorize.xaml.cs"> + <DependentUpon>Authorize.xaml</DependentUpon> + </Compile> <Compile Include="InMemoryTokenManager.cs" /> <Compile Include="Properties\AssemblyInfo.cs"> <SubType>Code</SubType> @@ -131,4 +147,4 @@ <Target Name="AfterBuild"> </Target> --> -</Project> +</Project>
\ No newline at end of file diff --git a/samples/OAuthServiceProvider/App_Code/CustomOAuthTypeProvider.cs b/samples/OAuthServiceProvider/App_Code/CustomOAuthTypeProvider.cs index 9fdbf29..0932dec 100644 --- a/samples/OAuthServiceProvider/App_Code/CustomOAuthTypeProvider.cs +++ b/samples/OAuthServiceProvider/App_Code/CustomOAuthTypeProvider.cs @@ -15,7 +15,8 @@ public class CustomOAuthMessageFactory : OAuthServiceProviderMessageFactory { /// Initializes a new instance of the <see cref="CustomOAuthMessageFactory"/> class. /// </summary> /// <param name="tokenManager">The token manager instance to use.</param> - public CustomOAuthMessageFactory(IServiceProviderTokenManager tokenManager) : base(tokenManager) { + public CustomOAuthMessageFactory(IServiceProviderTokenManager tokenManager) + : base(tokenManager) { } public override IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) { @@ -23,7 +24,7 @@ public class CustomOAuthMessageFactory : OAuthServiceProviderMessageFactory { // inject our own type here to replace the standard one if (message is UnauthorizedTokenRequest) { - message = new RequestScopedTokenMessage(recipient); + message = new RequestScopedTokenMessage(recipient, message.Version); } return message; diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml index 0b54d0d..651de9f 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml @@ -25,6 +25,9 @@ <Column Name="ConsumerId" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> <Column Name="ConsumerKey" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> <Column Name="ConsumerSecret" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> + <Column Name="Callback" Type="System.String" CanBeNull="true" /> + <Column Name="VerificationCodeFormat" Type="DotNetOpenAuth.OAuth.VerificationCodeFormat" CanBeNull="false" /> + <Column Name="VerificationCodeLength" Type="System.Int32" CanBeNull="false" /> <Association Name="OAuthConsumer_OAuthToken" Member="OAuthTokens" ThisKey="ConsumerId" OtherKey="ConsumerId" Type="OAuthToken" /> </Type> </Table> @@ -33,11 +36,14 @@ <Column Name="TokenId" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> <Column Name="Token" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> <Column Name="TokenSecret" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> - <Column Name="State" Type="TokenAuthorizationState" DbType="Int NOT NULL" CanBeNull="false" /> + <Column Name="State" Type="TokenAuthorizationState" DbType="INT NOT NULL" CanBeNull="false" /> <Column Name="IssueDate" Type="System.DateTime" DbType="DateTime NOT NULL" CanBeNull="false" /> <Column Name="ConsumerId" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" /> <Column Name="UserId" Type="System.Int32" DbType="Int" CanBeNull="true" /> <Column Name="Scope" Type="System.String" DbType="nvarchar(MAX)" CanBeNull="false" /> + <Column Name="RequestTokenVerifier" Type="System.String" CanBeNull="true" /> + <Column Name="RequestTokenCallback" Type="System.String" CanBeNull="true" /> + <Column Name="ConsumerVersion" Type="System.String" CanBeNull="true" /> <Association Name="OAuthConsumer_OAuthToken" Member="OAuthConsumer" ThisKey="ConsumerId" OtherKey="ConsumerId" Type="OAuthConsumer" IsForeignKey="true" DeleteRule="CASCADE" DeleteOnNull="true" /> <Association Name="User_OAuthToken" Member="User" ThisKey="UserId" OtherKey="UserId" Type="User" IsForeignKey="true" DeleteRule="CASCADE" /> </Type> diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout index 1fc61cf..71bd4aa 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout @@ -14,16 +14,16 @@ <elementListCompartment Id="eba736b9-f9ec-484b-8083-c77155a49e4e" absoluteBounds="3.515, 1.085, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> </nestedChildShapes> </classShape> - <classShape Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" absoluteBounds="0.625, 3.25, 2, 1.3862939453124998"> + <classShape Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" absoluteBounds="0.625, 3.25, 2, 1.9631982421874996"> <DataClassMoniker Name="/DataClassesDataContext/OAuthConsumer" /> <nestedChildShapes> - <elementListCompartment Id="464308c4-d112-4448-b0c9-d9b82fb0ca4e" absoluteBounds="0.64, 3.71, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> + <elementListCompartment Id="464308c4-d112-4448-b0c9-d9b82fb0ca4e" absoluteBounds="0.64, 3.71, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> </nestedChildShapes> </classShape> - <classShape Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" absoluteBounds="3.5, 3.125, 2, 2.3478011067708326"> + <classShape Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" absoluteBounds="3.5, 3.125, 2, 2.9247054036458326"> <DataClassMoniker Name="/DataClassesDataContext/OAuthToken" /> <nestedChildShapes> - <elementListCompartment Id="403126d0-3d2a-4af4-b0b8-c489a830bbd4" absoluteBounds="3.515, 3.585, 1.9700000000000002, 1.7878011067708333" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> + <elementListCompartment Id="403126d0-3d2a-4af4-b0b8-c489a830bbd4" absoluteBounds="3.515, 3.585, 1.9700000000000002, 2.364705403645833" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> </nestedChildShapes> </classShape> <associationConnector edgePoints="[(2.625 : 1.31814697265625); (3.5 : 1.31814697265625)]" fixedFrom="NotFixed" fixedTo="NotFixed"> @@ -33,14 +33,14 @@ <classShapeMoniker Id="8a79b099-7f87-4766-907a-db2c3e1b5716" /> </nodes> </associationConnector> - <associationConnector edgePoints="[(2.625 : 3.94314697265625); (3.5 : 3.94314697265625)]" fixedFrom="Algorithm" fixedTo="Algorithm"> + <associationConnector edgePoints="[(2.625 : 4.23159912109375); (3.5 : 4.23159912109375)]" fixedFrom="Algorithm" fixedTo="Algorithm"> <AssociationMoniker Name="/DataClassesDataContext/OAuthConsumer/OAuthConsumer_OAuthToken" /> <nodes> <classShapeMoniker Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" /> <classShapeMoniker Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" /> </nodes> </associationConnector> - <associationConnector edgePoints="[(0.53125 : 2.27089680989583); (0.53125 : 5.08579752604167); (3.5 : 5.08579752604167)]" fixedFrom="Algorithm" fixedTo="Algorithm"> + <associationConnector edgePoints="[(0.53125 : 2.27089680989583); (0.53125 : 5.66270182291667); (3.5 : 5.66270182291667)]" fixedFrom="Algorithm" fixedTo="Algorithm"> <AssociationMoniker Name="/DataClassesDataContext/User/User_OAuthToken" /> <nodes> <classShapeMoniker Id="696d2c69-040e-411d-9257-bb664b743834" /> diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs b/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs index 2fc532e..b66e75f 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.3053 +// Runtime Version:2.0.50727.4918 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -483,6 +483,12 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha private string _ConsumerSecret; + private string _Callback; + + private DotNetOpenAuth.OAuth.VerificationCodeFormat _VerificationCodeFormat; + + private int _VerificationCodeLength; + private EntitySet<OAuthToken> _OAuthTokens; #region Extensibility Method Definitions @@ -495,6 +501,12 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha partial void OnConsumerKeyChanged(); partial void OnConsumerSecretChanging(string value); partial void OnConsumerSecretChanged(); + partial void OnCallbackChanging(string value); + partial void OnCallbackChanged(); + partial void OnVerificationCodeFormatChanging(DotNetOpenAuth.OAuth.VerificationCodeFormat value); + partial void OnVerificationCodeFormatChanged(); + partial void OnVerificationCodeLengthChanging(int value); + partial void OnVerificationCodeLengthChanged(); #endregion public OAuthConsumer() @@ -563,6 +575,66 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha } } + [Column(Storage="_Callback")] + public string Callback + { + get + { + return this._Callback; + } + set + { + if ((this._Callback != value)) + { + this.OnCallbackChanging(value); + this.SendPropertyChanging(); + this._Callback = value; + this.SendPropertyChanged("Callback"); + this.OnCallbackChanged(); + } + } + } + + [Column(Storage="_VerificationCodeFormat")] + public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat + { + get + { + return this._VerificationCodeFormat; + } + set + { + if ((this._VerificationCodeFormat != value)) + { + this.OnVerificationCodeFormatChanging(value); + this.SendPropertyChanging(); + this._VerificationCodeFormat = value; + this.SendPropertyChanged("VerificationCodeFormat"); + this.OnVerificationCodeFormatChanged(); + } + } + } + + [Column(Storage="_VerificationCodeLength")] + public int VerificationCodeLength + { + get + { + return this._VerificationCodeLength; + } + set + { + if ((this._VerificationCodeLength != value)) + { + this.OnVerificationCodeLengthChanging(value); + this.SendPropertyChanging(); + this._VerificationCodeLength = value; + this.SendPropertyChanged("VerificationCodeLength"); + this.OnVerificationCodeLengthChanged(); + } + } + } + [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthTokens", ThisKey="ConsumerId", OtherKey="ConsumerId")] public EntitySet<OAuthToken> OAuthTokens { @@ -631,6 +703,12 @@ public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChange private string _Scope; + private string _RequestTokenVerifier; + + private string _RequestTokenCallback; + + private string _ConsumerVersion; + private EntityRef<OAuthConsumer> _OAuthConsumer; private EntityRef<User> _User; @@ -655,6 +733,12 @@ public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChange partial void OnUserIdChanged(); partial void OnScopeChanging(string value); partial void OnScopeChanged(); + partial void OnRequestTokenVerifierChanging(string value); + partial void OnRequestTokenVerifierChanged(); + partial void OnRequestTokenCallbackChanging(string value); + partial void OnRequestTokenCallbackChanged(); + partial void OnConsumerVersionChanging(string value); + partial void OnConsumerVersionChanged(); #endregion public OAuthToken() @@ -724,7 +808,7 @@ public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChange } } - [Column(Storage="_State", DbType="Int NOT NULL", CanBeNull=false)] + [Column(Storage="_State", DbType="INT NOT NULL", CanBeNull=false)] public TokenAuthorizationState State { get @@ -832,6 +916,66 @@ public partial class OAuthToken : INotifyPropertyChanging, INotifyPropertyChange } } + [Column(Storage="_RequestTokenVerifier")] + public string RequestTokenVerifier + { + get + { + return this._RequestTokenVerifier; + } + set + { + if ((this._RequestTokenVerifier != value)) + { + this.OnRequestTokenVerifierChanging(value); + this.SendPropertyChanging(); + this._RequestTokenVerifier = value; + this.SendPropertyChanged("RequestTokenVerifier"); + this.OnRequestTokenVerifierChanged(); + } + } + } + + [Column(Storage="_RequestTokenCallback")] + public string RequestTokenCallback + { + get + { + return this._RequestTokenCallback; + } + set + { + if ((this._RequestTokenCallback != value)) + { + this.OnRequestTokenCallbackChanging(value); + this.SendPropertyChanging(); + this._RequestTokenCallback = value; + this.SendPropertyChanged("RequestTokenCallback"); + this.OnRequestTokenCallbackChanged(); + } + } + } + + [Column(Storage="_ConsumerVersion")] + public string ConsumerVersion + { + get + { + return this._ConsumerVersion; + } + set + { + if ((this._ConsumerVersion != value)) + { + this.OnConsumerVersionChanging(value); + this.SendPropertyChanging(); + this._ConsumerVersion = value; + this.SendPropertyChanged("ConsumerVersion"); + this.OnConsumerVersionChanged(); + } + } + } + [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthConsumer", ThisKey="ConsumerId", OtherKey="ConsumerId", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")] public OAuthConsumer OAuthConsumer { diff --git a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs index 275a7c9..8ca4539 100644 --- a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs @@ -14,14 +14,22 @@ using DotNetOpenAuth.OAuth.Messages; public class DatabaseTokenManager : IServiceProviderTokenManager { #region IServiceProviderTokenManager - public string GetConsumerSecret(string consumerKey) { + public IConsumerDescription GetConsumer(string consumerKey) { var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault( consumerCandidate => consumerCandidate.ConsumerKey == consumerKey); if (consumerRow == null) { - throw new ArgumentException(); + throw new KeyNotFoundException(); } - return consumerRow.ConsumerSecret; + return consumerRow; + } + + public IServiceProviderRequestToken GetRequestToken(string token) { + try { + return Global.DataContext.OAuthTokens.First(t => t.Token == token); + } catch (InvalidOperationException ex) { + throw new KeyNotFoundException("Unrecognized token", ex); + } } #endregion @@ -51,6 +59,7 @@ public class DatabaseTokenManager : IServiceProviderTokenManager { }; Global.DataContext.OAuthTokens.InsertOnSubmit(newToken); + Global.DataContext.SubmitChanges(); } /// <summary> diff --git a/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs b/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs new file mode 100644 index 0000000..1255717 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthConsumer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public partial class OAuthConsumer : IConsumerDescription { + #region IConsumerDescription Members + + string IConsumerDescription.Key { + get { return this.ConsumerKey; } + } + + string IConsumerDescription.Secret { + get { return this.ConsumerSecret; } + } + + System.Security.Cryptography.X509Certificates.X509Certificate2 IConsumerDescription.Certificate { + get { return null; } + } + + Uri IConsumerDescription.Callback { + get { return this.Callback != null ? new Uri(this.Callback) : null; } + } + + DotNetOpenAuth.OAuth.VerificationCodeFormat IConsumerDescription.VerificationCodeFormat { + get { return this.VerificationCodeFormat; } + } + + int IConsumerDescription.VerificationCodeLength { + get { return this.VerificationCodeLength; } + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs new file mode 100644 index 0000000..ec9b31e --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public partial class OAuthToken : IServiceProviderRequestToken { + #region IServiceProviderRequestToken Members + + string IServiceProviderRequestToken.Token { + get { return this.Token; } + } + + string IServiceProviderRequestToken.ConsumerKey { + get { return this.OAuthConsumer.ConsumerKey; } + } + + DateTime IServiceProviderRequestToken.CreatedOn { + get { return this.IssueDate; } + } + + Uri IServiceProviderRequestToken.Callback { + get { return new Uri(this.RequestTokenCallback); } + set { this.RequestTokenCallback = value.AbsoluteUri; } + } + + string IServiceProviderRequestToken.VerificationCode { + get { return this.RequestTokenVerifier; } + set { this.RequestTokenVerifier = value; } + } + + Version IServiceProviderRequestToken.ConsumerVersion { + get { return new Version(this.ConsumerVersion); } + set { this.ConsumerVersion = value.ToString(); } + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/App_Code/RequestScopedTokenMessage.cs b/samples/OAuthServiceProvider/App_Code/RequestScopedTokenMessage.cs index b33a734..4cc4860 100644 --- a/samples/OAuthServiceProvider/App_Code/RequestScopedTokenMessage.cs +++ b/samples/OAuthServiceProvider/App_Code/RequestScopedTokenMessage.cs @@ -1,4 +1,5 @@ -using DotNetOpenAuth.Messaging; +using System; +using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth.Messages; /// <summary> @@ -9,7 +10,8 @@ public class RequestScopedTokenMessage : UnauthorizedTokenRequest { /// Initializes a new instance of the <see cref="RequestScopedTokenMessage"/> class. /// </summary> /// <param name="endpoint">The endpoint that will receive the message.</param> - public RequestScopedTokenMessage(MessageReceivingEndpoint endpoint) : base(endpoint) { + /// <param name="version">The OAuth version.</param> + public RequestScopedTokenMessage(MessageReceivingEndpoint endpoint, Version version) : base(endpoint, version) { } /// <summary> diff --git a/samples/OAuthServiceProvider/Members/Authorize.aspx b/samples/OAuthServiceProvider/Members/Authorize.aspx index 69f9498..2f5edf1 100644 --- a/samples/OAuthServiceProvider/Members/Authorize.aspx +++ b/samples/OAuthServiceProvider/Members/Authorize.aspx @@ -8,23 +8,31 @@ <b>Warning</b>: Never give your login credentials to another web site or application. </div> <asp:HiddenField runat="server" ID="OAuthAuthorizationSecToken" EnableViewState="false" /> - <p>The client web site or application - <asp:Label ID="consumerLabel" Font-Bold="true" runat="server" Text="[consumer]" /> - wants access to your - <asp:Label ID="desiredAccessLabel" Font-Bold="true" runat="server" Text="[protected resource]" />. - </p> + <p>The client web site or application <asp:Label ID="consumerLabel" Font-Bold="true" + runat="server" Text="[consumer]" /> wants access to your <asp:Label ID="desiredAccessLabel" + Font-Bold="true" runat="server" Text="[protected resource]" />. </p> <p>Do you want to allow this? </p> <div> <asp:Button ID="allowAccessButton" runat="server" Text="Yes" OnClick="allowAccessButton_Click" /> - <asp:Button ID="denyAccessButton" runat="server" Text="No" - onclick="denyAccessButton_Click" /> + <asp:Button ID="denyAccessButton" runat="server" Text="No" OnClick="denyAccessButton_Click" /> </div> <p>If you grant access now, you can revoke it at any time by returning to this page. </p> + <asp:Panel runat="server" BackColor="Red" ForeColor="White" Font-Bold="true" Visible="false" ID="OAuth10ConsumerWarning"> + This website is registered with service_PROVIDER_DOMAIN_NAME to make authorization requests, but has not been configured to send requests securely. If you grant access but you did not initiate this request at consumer_DOMAIN_NAME, it may be possible for other users of consumer_DOMAIN_NAME to access your data. We recommend you deny access unless you are certain that you initiated this request directly with consumer_DOMAIN_NAME. + </asp:Panel> </asp:View> <asp:View runat="server"> - <p>Authorization has been granted. Please inform the consumer application or web site - of this. </p> + <p>Authorization has been granted.</p> + <asp:MultiView runat="server" ID="verifierMultiView" ActiveViewIndex="0"> + <asp:View runat="server"> + <p>You must enter this verification code at the Consumer: <asp:Label runat="server" + ID="verificationCodeLabel" /> </p> + </asp:View> + <asp:View ID="View1" runat="server"> + <p>You may now close this window and return to the Consumer. </p> + </asp:View> + </asp:MultiView> </asp:View> <asp:View runat="server"> <p>Authorization has been denied. You're free to do whatever now. </p> diff --git a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs index b3094c9..f936c60 100644 --- a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs +++ b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs @@ -37,6 +37,8 @@ public partial class Authorize : System.Web.UI.Page { CryptoRandomDataGenerator.GetBytes(randomData); this.AuthorizationSecret = Convert.ToBase64String(randomData); OAuthAuthorizationSecToken.Value = this.AuthorizationSecret; + + OAuth10ConsumerWarning.Visible = Global.PendingOAuthAuthorization.IsUnsafeRequest; } } } @@ -54,6 +56,15 @@ public partial class Authorize : System.Web.UI.Page { var response = sp.PrepareAuthorizationResponse(pending); if (response != null) { sp.Channel.Send(response); + } else { + if (pending.IsUnsafeRequest) { + verifierMultiView.ActiveViewIndex = 1; + } else { + string verifier = ServiceProvider.CreateVerificationCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 10); + verificationCodeLabel.Text = verifier; + ITokenContainingMessage requestTokenMessage = pending; + Global.TokenManager.GetRequestToken(requestTokenMessage.Token).VerificationCode = verifier; + } } } diff --git a/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx b/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx index de82304..df29914 100644 --- a/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx +++ b/samples/OpenIdRelyingPartyWebForms/ajaxlogin.aspx @@ -8,7 +8,7 @@ { width: 200px; } -.openidtextbox +input.openidtextbox { width: 185px; } diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index 591c442..231d92d 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -167,9 +167,11 @@ <Compile Include="OAuth\ChannelElements\OAuthChannelTests.cs" /> <Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElementTest.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementBaseTests.cs" /> + <Compile Include="OAuth\ChannelElements\UriOrOobEncodingTests.cs" /> <Compile Include="OAuth\ConsumerDescription.cs" /> <Compile Include="OAuth\ProtocolTests.cs" /> <Compile Include="OAuth\ServiceProviderDescriptionTests.cs" /> + <Compile Include="OAuth\ServiceProviderTests.cs" /> <Compile Include="OpenId\AssociationsTests.cs" /> <Compile Include="OpenId\AssociationTests.cs" /> <Compile Include="OpenId\AuthenticationTests.cs" /> diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index be3c563..6980761 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -9,12 +9,13 @@ namespace DotNetOpenAuth.Test.Mocks { using System.Collections.Generic; using System.Diagnostics; using System.Linq; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { - private Dictionary<string, string> consumersAndSecrets = new Dictionary<string, string>(); - private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>(); + private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key); + private KeyedCollectionDelegate<string, TokenInfo> tokens = new KeyedCollectionDelegate<string, TokenInfo>(t => t.Token); /// <summary> /// Request tokens that have been issued, and whether they have been authorized yet. @@ -29,11 +30,11 @@ namespace DotNetOpenAuth.Test.Mocks { #region IConsumerTokenManager Members public string ConsumerKey { - get { return this.consumersAndSecrets.Keys.Single(); } + get { return this.consumers.Single().Key; } } public string ConsumerSecret { - get { return this.consumersAndSecrets.Values.Single(); } + get { return this.consumers.Single().Secret; } } #endregion @@ -41,11 +42,11 @@ namespace DotNetOpenAuth.Test.Mocks { #region ITokenManager Members public string GetTokenSecret(string token) { - return this.tokensAndSecrets[token]; + return this.tokens[token].Secret; } public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) { - this.tokensAndSecrets[response.Token] = response.TokenSecret; + this.tokens.Add(new TokenInfo { ConsumerKey = request.ConsumerKey, Token = response.Token, Secret = response.TokenSecret }); this.requestTokens.Add(response.Token, false); } @@ -70,8 +71,8 @@ namespace DotNetOpenAuth.Test.Mocks { ////Debug.Assert(this.requestTokens[requestToken], "Unauthorized token should not be exchanged for access token."); this.requestTokens.Remove(requestToken); this.accessTokens.Add(accessToken); - this.tokensAndSecrets.Remove(requestToken); - this.tokensAndSecrets[accessToken] = accessTokenSecret; + this.tokens.Remove(requestToken); + this.tokens.Add(new TokenInfo { Token = accessToken, Secret = accessTokenSecret }); } /// <summary> @@ -93,8 +94,12 @@ namespace DotNetOpenAuth.Test.Mocks { #region IServiceProviderTokenManager Members - public string GetConsumerSecret(string consumerKey) { - return this.consumersAndSecrets[consumerKey]; + public IConsumerDescription GetConsumer(string consumerKey) { + return this.consumers[consumerKey]; + } + + public IServiceProviderRequestToken GetRequestToken(string token) { + return this.tokens[token]; } #endregion @@ -105,7 +110,7 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> /// <param name="consumerDescription">The consumer description.</param> internal void AddConsumer(ConsumerDescription consumerDescription) { - this.consumersAndSecrets.Add(consumerDescription.ConsumerKey, consumerDescription.ConsumerSecret); + this.consumers.Add(new ConsumerInfo { Key = consumerDescription.ConsumerKey, Secret = consumerDescription.ConsumerSecret }); } /// <summary> @@ -119,5 +124,43 @@ namespace DotNetOpenAuth.Test.Mocks { this.requestTokens[requestToken] = true; } + + private class TokenInfo : IServiceProviderRequestToken { + internal TokenInfo() { + this.CreatedOn = DateTime.Now; + } + + public string ConsumerKey { get; set; } + + public DateTime CreatedOn { get; set; } + + public string Token { get; set; } + + public string VerificationCode { get; set; } + + public Uri Callback { get; set; } + + public Version ConsumerVersion { get; set; } + + internal string Secret { get; set; } + } + + private class ConsumerInfo : IConsumerDescription { + #region IConsumerDescription Members + + public string Key { get; set; } + + public string Secret { get; set; } + + public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get; set; } + + public Uri Callback { get; set; } + + public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat { get; set; } + + public int VerificationCodeLength { get; set; } + + #endregion + } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index bc3a94c..856f164 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { base.SetUp(); this.webRequestHandler = new TestWebRequestHandler(); - this.signingElement = new RsaSha1SigningBindingElement(); + this.signingElement = new RsaSha1SigningBindingElement(new InMemoryTokenManager()); this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge); this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory()); this.accessor = OAuthChannel_Accessor.AttachShadow(this.channel); @@ -47,22 +47,22 @@ namespace DotNetOpenAuth.Test.ChannelElements { [TestMethod, ExpectedException(typeof(ArgumentNullException))] public void CtorNullStore() { - new OAuthChannel(new RsaSha1SigningBindingElement(), null, new InMemoryTokenManager(), new TestMessageFactory()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), null, new InMemoryTokenManager(), new TestMessageFactory()); } [TestMethod, ExpectedException(typeof(ArgumentNullException))] public void CtorNullTokenManager() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, null, new TestMessageFactory()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, null, new TestMessageFactory()); } [TestMethod] public void CtorSimpleConsumer() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); } [TestMethod] public void CtorSimpleServiceProvider() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); } [TestMethod] diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs index ca63b50..627db8f 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs @@ -4,9 +4,9 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements -{ +namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,7 +17,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; Assert.IsNotNull(target.ProcessOutgoingMessage(message)); @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureVerification() { MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); ITamperProtectionChannelBindingElement target = new PlaintextSigningBindingElement(); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "PLAINTEXT"; @@ -41,7 +41,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureVerificationNotApplicable() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "ANOTHERALGORITHM"; @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; @@ -67,7 +67,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpSignatureVerification() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "PLAINTEXT"; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 93c0b3f..6e566c8 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; + using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -63,7 +64,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) { endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); - UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint); + UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint, Protocol.V10.Version); message.ConsumerKey = "nerdbank.org"; ((ITamperResistantOAuthMessage)message).ConsumerSecret = "nerdbanksecret"; var signedMessage = (ITamperResistantOAuthMessage)message; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs new file mode 100644 index 0000000..40fc93e --- /dev/null +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------- +// <copyright file="UriOrOobEncodingTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth.ChannelElements; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class UriOrOobEncodingTests : TestBase { + private UriOrOobEncoding encoding; + + [TestInitialize] + public void Setup() { + this.encoding = new UriOrOobEncoding(); + } + + /// <summary> + /// Verifies null value encoding + /// </summary> + [TestMethod] + public void NullValueEncoding() { + Assert.AreEqual("oob", this.encoding.EncodedNullValue); + } + + /// <summary> + /// Verifies decoding "oob" results in a null uri. + /// </summary> + [TestMethod] + public void DecodeOobToNullUri() { + Assert.IsNull(this.encoding.Decode("oob")); + } + + /// <summary> + /// Verifies that decoding an empty string generates an exception. + /// </summary> + [TestMethod, ExpectedException(typeof(UriFormatException))] + public void DecodeEmptyStringFails() { + this.encoding.Decode(string.Empty); + } + + /// <summary> + /// Verifies proper decoding/encoding of a Uri + /// </summary> + [TestMethod] + public void UriEncodeDecode() { + Uri original = new Uri("http://somehost/p?q=a#frag"); + string encodedValue = this.encoding.Encode(original); + Assert.AreEqual(original.AbsoluteUri, encodedValue); + Uri decoded = (Uri)this.encoding.Decode(encodedValue); + Assert.AreEqual(original, decoded); + } + + /// <summary> + /// Verifies failure to decode a relative Uri + /// </summary> + [TestMethod, ExpectedException(typeof(UriFormatException))] + public void RelativeUriDecodeFails() { + this.encoding.Decode("../a/b"); + } + } +} diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs index 6a2551a..ce8070b 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs @@ -12,7 +12,7 @@ namespace DotNetOpenAuth.Test { public class ProtocolTests { [TestMethod] public void Default() { - Assert.AreSame(Protocol.V10, Protocol.Default); + Assert.AreSame(Protocol.V10a, Protocol.Default); } [TestMethod] @@ -23,12 +23,12 @@ namespace DotNetOpenAuth.Test { [TestMethod] public void AuthorizationHeaderScheme() { - Assert.AreEqual("OAuth", Protocol.V10.AuthorizationHeaderScheme); + Assert.AreEqual("OAuth", Protocol.AuthorizationHeaderScheme); } [TestMethod] public void ParameterPrefix() { - Assert.AreEqual("oauth_", Protocol.V10.ParameterPrefix); + Assert.AreEqual("oauth_", Protocol.ParameterPrefix); } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs new file mode 100644 index 0000000..2a443ce --- /dev/null +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------- +// <copyright file="ServiceProviderTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OAuth { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class ServiceProviderTests : TestBase { + /// <summary> + /// Verifies the CreateVerificationCode method. + /// </summary> + [TestMethod] + public void CreateVerificationCode() { + this.TestCode(VerificationCodeFormat.Numeric, 3, MessagingUtilities.Digits); + this.TestCode(VerificationCodeFormat.AlphaLower, 5, MessagingUtilities.LowercaseLetters); + this.TestCode(VerificationCodeFormat.AlphaUpper, 5, MessagingUtilities.UppercaseLetters); + this.TestCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 8, MessagingUtilities.AlphaNumericNoLookAlikes); + } + + private void TestCode(VerificationCodeFormat format, int length, string allowableCharacters) { + string code = ServiceProvider.CreateVerificationCode(format, length); + TestContext.WriteLine("{0} of length {2}: {1}", format, code, length); + Assert.AreEqual(length, code.Length); + foreach (char ch in code) { + Assert.IsTrue(allowableCharacters.Contains(ch)); + } + } + } +} diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index 81bec05..335d0e1 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -17,6 +17,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}" ProjectSection(SolutionItems) = preProject ..\doc\specs\OAuth Core 1.0.htm = ..\doc\specs\OAuth Core 1.0.htm + ..\doc\specs\OAuth Core 1.0a (Draft 3).htm = ..\doc\specs\OAuth Core 1.0a (Draft 3).htm ..\doc\specs\OpenID OAuth Extension.htm = ..\doc\specs\OpenID OAuth Extension.htm ..\doc\specs\openid-attribute-exchange-1_0.html = ..\doc\specs\openid-attribute-exchange-1_0.html ..\doc\specs\openid-authentication-1_1.html = ..\doc\specs\openid-authentication-1_1.html diff --git a/src/DotNetOpenAuth.vsmdi b/src/DotNetOpenAuth.vsmdi index 249adb5..5edab72 100644 --- a/src/DotNetOpenAuth.vsmdi +++ b/src/DotNetOpenAuth.vsmdi @@ -17,44 +17,43 @@ <TestLink id="1757957f-17bb-ef9f-39f8-c008863ec033" name="AssuranceLevels" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="6fbd433d-cd54-b206-6df3-fbd591690a4d" name="HtmlDiscover_11" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d083396b-db68-1318-e62b-6dc9f89e26bd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="48115dc0-1323-bab0-c540-695a2160e0a3" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5211652f-1c25-fd4b-890d-05d2178a60e2" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="58d69d1e-3bd2-3379-0af1-188f9cff2dd0" name="IsTypeUriPresentEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="cb48421f-f4ff-3994-3abc-4be35f8bfd99" name="AssociateQuietlyFailsAfterHttpError" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2599d559-d036-5dd2-0b5b-fb229c3bf486" name="InvalidRealmBadWildcard2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="bdba0004-be80-f5c1-1aae-487db09bdf04" name="GetReturnToArgumentDoesNotReturnExtraArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="8fd673c8-977a-7b66-72cb-38c7054796c7" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0290975f-02ce-d8a7-d723-5dae623cab46" name="CtorNullTokenManager" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="21cf1f9a-063f-395a-f8aa-92c190c69146" name="SignaturesMatchKnownGood" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="12071fbc-202e-05a8-655a-b21917615b5e" name="IsReturnUrlDiscoverable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="715dcbdd-28f5-3c33-7d88-e0a1b648d89a" name="CreateRequestDumbMode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="46ec24da-deb7-27c7-6dc6-52090e4fd1fb" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9d4a230d-9e74-dc1b-ecdc-bf875b56e1b3" name="CtorNullVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="04be6602-31a2-f4ae-8fdb-b9ad2ac370be" name="PrepareMessageForReceiving" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="90557d85-db17-e9ab-e17b-32d6cc9fd437" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="20646985-c84a-db8e-f982-ec55d61eaacd" name="ResponseNonceSetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="248f0afc-979f-a86f-e7de-fdeb4f9dd3ea" name="CtorBadUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="054484ce-12c5-83ad-49a4-b241cd81557d" name="ClaimedIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1f46ce86-bc66-3f5c-4061-3f851cf6dd7f" name="HtmlDiscover_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="bb68f1dc-3854-fc11-2ea6-d45f892d76fa" name="NistAssuranceLevelSetVarious" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="afafb5ef-662e-2da3-35b8-1d67bb0d79ce" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="704a32d0-3f50-d462-f767-fd9cf1981b7f" name="ProviderVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="25e2c593-2e69-6215-90c0-67f269939865" name="CtorEmptyTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="de1cdd00-a226-0d43-62b6-0c1ad325be8c" name="RequiredMinAndMaxVersions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="63e5025b-5ccf-5f13-6e05-d1e44502a6e9" name="RequestBadPreferredScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> @@ -66,109 +65,106 @@ <TestLink id="4a00f3ab-f405-95a7-d745-2fcf7787eb56" name="GetNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ed7efca3-c3c1-bc4a-cef7-eaf984749355" name="ValidMessageReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f362baf3-da5b-1b8c-39ae-7c9b2051270a" name="AuthenticationTimeUtcSetUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="643d722c-2c2b-fbd8-a499-5a852ef14dc7" name="PrepareMessageForSending" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5a0d31d9-9c70-2a28-3e8c-46e8e047ac2d" name="ReceiveNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="8bbc6a02-b5a4-ea8e-2a77-8d1b6671ceb5" name="ImplicitConverstionFromUriTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="77e5af06-b02d-692e-b32f-40ea39e77fbd" name="FriendlyIdentifierForDisplay" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d6951a97-9d0b-31c1-7a29-01fbb986c5a9" name="SpoofedClaimedIdDetectionSolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="decb3fef-ef61-6794-5bc6-f7ff722a146e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f20bd439-e277-dc27-4ec4-5d5949d1c6bf" name="RequestUsingAuthorizationHeaderScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b671ea40-3b8c-58d5-7788-f776810c49be" name="UnicodeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ca5360e1-ca08-d00f-6ade-7c9441db4294" name="CreateQueryStringEmptyCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="98a2ece8-c9e6-e6f3-c65e-f915b22077fa" name="RequestUsingGet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0dc9284e-cba4-9d87-8955-19639578c70d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="34357633-4745-6fba-9316-493d3c6c5b90" name="ParseEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5f6e9836-2630-b53c-4121-64900bde5628" name="IsExtensionSupportedNullExtension" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7f9c4a9e-de7a-555c-543d-db89b757588e" name="AppendQueryArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c23e762d-4162-cb9e-47b3-455a568b5072" name="SendIndirectMessageFormPostEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0f80456a-5465-dd68-bfb0-ba27b676187c" name="EqualsTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="53cbbf4a-89d3-122b-0d88-662f3022ce26" name="OpenIdMaxAuthenticationTime" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="03e293d0-dbe8-ad09-1ddd-de7be2cf9276" name="CopyTo" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="313faac6-6357-5468-2d4d-4c9fba001678" name="TryParseNoThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b2e1bba0-ab24-cdd5-906c-a3655814ab2d" name="SendSetsTimestamp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="aef95d4e-ad69-0eca-6528-7fce78512336" name="EqualityTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="864578a5-61a2-bc5d-1d19-17093885bea3" name="InvalidRealmTwoWildcards1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5803e93d-e256-86ba-e10e-499d2f813c6d" name="Trivial" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="00ed61cd-46cd-9c0e-f044-38d784c8bcfb" name="DecodeEmptyStringFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="29e45877-ca7a-85de-5c39-6d43befe1a1e" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1f3ea08b-9880-635f-368f-9fcd3e25f3cd" name="ReadFromRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f1e1aa37-c712-6096-22fa-394008f0820a" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e9bc3f63-aeb1-d84d-8abc-fc6ed77955e6" name="SignedResponsesIncludeExtraDataInSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="10c44d72-2789-2afe-3b27-091dea97546e" name="RequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="9f880280-aa8f-91bb-4a5f-3fe044b6815a" name="CreateVerificationCode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="121983e3-1336-70cb-8d2a-498629e92bec" name="GetReturnToArgumentNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="13acd546-c82e-324c-220d-34f42a6d705e" name="DeserializeSimple" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="49a266cf-4ab6-3fdc-f4fd-21533f42c7cb" name="CtorWithProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="32532d1f-d817-258d-ca72-021772bfc185" name="UriEncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7debb527-142a-6ca6-3b9b-1e131c18e801" name="AccessTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1c531011-403a-0821-d630-d5433d968f31" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5fa10f12-3de5-1783-0a97-9802d5469dfa" name="AddAttributeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="149a95cf-a538-f853-e11b-3133c15579c5" name="RequestTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f4b313bb-cebc-a854-ffbd-6c955d850a05" name="VerifyGoodTimestampIsAccepted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="dd9e3279-2d7e-e88e-ccfa-ef213055fc3d" name="SendDirectedNoRecipientMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="77e5af06-b02d-692e-b32f-40ea39e77fbd" name="FriendlyIdentifierForDisplay" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="782d64c8-46af-a624-b3f6-a65aeaa57bfe" name="LastLineNotTerminatedLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="47e8fae9-542d-1ebb-e17c-568cf9594539" name="RelativeUriDecodeFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="75fa4664-bb0e-3a54-de29-c18ac712b231" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c11e5541-0a92-85ab-4f90-0db7766ebdcb" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="8df5d75f-bd4d-ce4e-2faf-6106b623de42" name="AddAttributeRequestStrangeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c2c78c43-7f50-ffc3-affb-e60de2b76c94" name="CreateQueryStringNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fba4d9a6-d8c7-a063-7c07-4a27c38c94a9" name="InvalidRealmBadWildcard3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f41ce7ab-5500-7eea-ab4d-8c646bffff23" name="HttpSchemePrepended" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="abb0610a-c06f-0767-ac99-f37a2b573d1b" name="ParameterPrefix" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="7650ec62-b144-f36f-8b56-31ad20521d0e" name="DoesNotStripFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="aa79cdf5-e0bc-194e-fdbb-78369c19c30f" name="ConstantFieldMemberInvalidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e2b1ae2a-8f30-b6b3-bca6-ef28fc5a0175" name="ClaimedIdAndLocalIdSpecifiedIsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a59c5dc0-de4d-8136-8545-2e2e9616de46" name="SerializationWithXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e2ab77b2-a6dc-f165-1485-140b9b3d916f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d66a3b7a-1738-f6b3-aed1-e9bc80734ae9" name="CtorNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f6ecb459-cc64-36ee-438c-4514e9413586" name="AddAttributeByPrimitives" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="051a85ed-eef9-9437-507d-d6208b6a8f74" name="DiscoveryWithRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="4a5b601d-475d-e6cc-1fec-19a2850681ad" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fb91e9dd-fc3b-d8a7-a5d7-d215d5ba880f" name="CtorStringHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="beb086e9-5eb7-fb8f-480a-70ede9efd70d" name="CreateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="59295023-d248-e9c4-68b9-65f6ea38490c" name="VerifyArgumentNotNullDoesNotThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="00089858-d849-1e5f-4fb5-31d8d0590233" name="VerifyArgumentNotNullThrows" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e78ab82c-3b49-468a-b2ad-ca038e98ff07" name="GetEnumerator" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="715dcbdd-28f5-3c33-7d88-e0a1b648d89a" name="CreateRequestDumbMode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="352d9fd6-cf38-4b72-478f-e3e17ace55f5" name="NoValueLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="62c6ee5b-ac29-461c-2373-bf620e948825" name="InvalidRealmNoScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="63944cb8-4c61-c42c-906f-986fa793370b" name="SignatureTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> @@ -176,88 +172,81 @@ <TestLink id="93c157e8-1293-3aff-f616-66502872b37d" name="DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e137b84a-d2a7-9af6-d15d-a92417668ccf" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5218fba2-d1af-e1f4-7641-9ae1d4975430" name="DirectResponsesSentUsingKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="643c9887-3f12-300e-fdac-17ae59652712" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7c048c58-c456-3406-995f-adb742cc2501" name="DeserializeInvalidMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="06b350b0-79d1-9393-7620-cd919061898c" name="ParseEndUserSuppliedUriIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f4893153-bb84-bf45-7889-8350a7e1db66" name="DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="6ef9df5a-d069-0103-5260-593808f232da" name="XrdsDiscoveryFromHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b9cda1a0-83cd-cf4b-b61f-4faa75fa37ba" name="ReceivedReplayProtectedMessageTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1deb0ca9-923a-8ef7-7a24-d5d5af04acdf" name="SpecAppendixAExample" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b70b4bd5-6dae-b4ad-349c-c3ad70603773" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1e2ae78c-d2f3-a808-2b82-eca9f9f2e458" name="Keys" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5716de48-f311-944f-45ff-872d68bc2bcf" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1dcbaac6-0b11-8d8f-50d7-237574abbab1" name="ToDictionaryWithSkippedNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d570770a-74e4-50ec-8eb9-91bd81c093ad" name="ParseNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3b535521-90c8-7f49-545f-bcfc4ad16d40" name="UnresponsiveProvidersComeLast" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b71e8878-b20e-5d96-bce4-7f10831ceaf8" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0443f5f8-aa08-80d5-dcc6-261802debe5a" name="XrdsDirectDiscovery_10" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a1ff4ada-fe5d-d2f3-b7fb-8e72db02b3c3" name="Full" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2d82ac4b-99b4-a132-eb62-d943e02d1498" name="ApplyHeadersToResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="997253fb-7591-c151-1705-02976b400f27" name="AddAttributeTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="97f0277a-86e6-5b5a-8419-c5253cabf2e0" name="UserAuthorizationUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e97cee09-4163-d83f-f65f-14e424294172" name="ExtensionsAreIdentifiedAsSignedOrUnsigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f49bcd49-76fb-bfea-b119-4e0f70159f80" name="OpenIdProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5aa4dfa9-9691-bfe0-7d81-587cfa519a55" name="DirectResponsesReceivedAsKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5c66a1b8-5b20-2e3b-8427-d6ff4640ac53" name="BadRequestsGenerateValidErrorResponses" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="03b47440-3d09-ab28-97f1-39809f5703b6" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="77047207-0571-72d5-71bd-586b878bcc0c" name="Base64Member" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="1e2ae78c-d2f3-a808-2b82-eca9f9f2e458" name="Keys" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="4bd86299-18d7-abbe-e5d2-1afad17279e9" name="Parse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a778f331-f14e-9d6e-f942-a023423540f6" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="495dd486-08dd-d365-7a84-67d96fef8460" name="SendIndirectedUndirectedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="5716de48-f311-944f-45ff-872d68bc2bcf" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="24506e06-a678-66cc-48ee-b7f11f18a6e8" name="StripXriScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d0a92f93-9bb4-1821-81cf-e9b50e3e7d62" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="0443f5f8-aa08-80d5-dcc6-261802debe5a" name="XrdsDirectDiscovery_10" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d5f4e610-eabe-1dc0-ab3f-7c9dcb17fcc3" name="CtorImpliedLocalIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f49bcd49-76fb-bfea-b119-4e0f70159f80" name="OpenIdProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="577b8522-8516-4f62-22db-76227bf82f4c" name="UserSetupUrlNotRequiredInV1SetupOrV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0215f125-3936-484e-a8d0-d940d85bbc27" name="AppendQueryArgsNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="cbfeb75b-d031-7df3-c281-3c9e1c450042" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="182203f3-5a16-b736-ea8c-b59f6bf7df66" name="InvalidRealmTwoWildcards2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f44fb549-fc8a-7469-6eed-09d9f86cebff" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="beb086e9-5eb7-fb8f-480a-70ede9efd70d" name="CreateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e9c2087b-1c52-5bb9-bf4e-9046cf281e36" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="38239ff0-1dfd-1116-55df-2790243dc768" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b63c4b89-3889-6dcf-8890-c92fc44c0b10" name="VerifyBadTimestampIsRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="58f848e5-42d7-1508-f9b5-7691337e6da9" name="IsExtensionSupportedEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="cb9a8325-abf5-5d97-a94e-a6d34f2b51e1" name="AssociateRenegotiateLimitedByRPSecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2495fc9b-d766-5ae7-7324-f044c4ce1242" name="AddNullValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="937b85f4-1ef3-84d1-a567-8bba079a33a9" name="Properties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> @@ -265,33 +254,39 @@ <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="32604ca2-2577-9c33-f778-ff7e4c985ce5" name="RequestTokenUriWithOAuthParametersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="82125083-ff5d-b6a4-61e0-a7f74eef41f7" name="RequireSslFiresEvent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5b4fee50-7c15-8c6b-3398-c82279646e5f" name="RequiredOptionalLists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ebd84587-bbc2-9889-c500-b6fbdf2bf209" name="GetRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2d0ee03a-f082-768c-a0db-574ac8efeffb" name="Valid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="72d3f240-67f2-0b04-bd31-a99c3f7a8e12" name="SharedAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="068dcefa-8f2b-52c3-fe79-576c84c5648b" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="dc6af36d-0efc-9291-a603-2af7bac2a269" name="ErrorMessagesAsHttp400" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ac4ff1af-8333-e54e-0322-27d8824d7573" name="RequestUsingAuthorizationHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="28fe030c-d36e-13cf-475c-7813210bf886" name="AddAttributeRequestAgain" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="64142858-d52e-be06-d11f-6be326c6176b" name="RespondTwoValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="910f8448-5454-8ae5-cba3-690c7f375576" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="507cd1b6-1010-0bca-cf7f-f96e3f4f6c6c" name="QueryBeforeSettingUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ef8a2274-4e58-0dde-4c5c-7f286865fc3a" name="SendReplayProtectedMessageSetsNonce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="734dd45c-6320-26a9-e412-62ecacfd285a" name="CtorNullAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="58f848e5-42d7-1508-f9b5-7691337e6da9" name="IsExtensionSupportedEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="21fa5a5c-c04b-831f-c537-fc8be1bf65b2" name="IsExtensionSupported" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9af943f7-b289-1a24-8e3e-bfbd7a55b4c7" name="CtorGoodUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e4403d9e-73c1-967d-345c-4a2c83880d4e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="9126d9e0-14dc-490b-3cd3-d3e424d38f9e" name="BinarySerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> @@ -301,63 +296,70 @@ <TestLink id="7c8eac5a-0455-e038-0e9a-10e59d459452" name="CtorUriHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f12bf351-584c-bc51-c315-a67f1076927c" name="ReturnToDoesNotMatchRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d3c4624f-f78a-2ff3-199a-77c922059718" name="Best" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b671ea40-3b8c-58d5-7788-f776810c49be" name="UnicodeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3d0effa3-894a-630c-02b0-ada4b5cef795" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5f3758b3-1410-c742-e623-b964c01b0633" name="AuthenticationTimeUtcConvertsToUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="547cfee6-bbb4-6138-a114-fc0eb6cc94f6" name="PrivateAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="385c302d-b546-c164-2a59-2e35f75d7d60" name="RemoveStructDeclaredProperty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="7650ec62-b144-f36f-8b56-31ad20521d0e" name="DoesNotStripFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="14acb719-f090-018f-b870-9a5acb1d7179" name="AddAuthLevelTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="90d3c411-8895-a07f-7a21-258b9d43c5b2" name="InvalidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="44091d36-98db-2115-8647-7bd7cd308796" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2f1a3fc4-77ec-2ae3-668c-9e18f9ab0ebe" name="SendIndirectMessage301Get" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f18b514c-4f78-5421-8bdf-8b0f1fdf2282" name="HandleLifecycle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="4b44b825-36cc-77f8-3a4a-5892c540f577" name="GetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b32b6295-d4a9-3369-f072-28a71e84d4e8" name="SerializationWithUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="115283b9-d95c-9a92-2197-96685ee8e96a" name="TwoExtensionsSameTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="da8fcfa9-bd2c-eca0-ecbf-90364f84e8e5" name="AddExtraFieldThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="be00d3ef-f24d-eb8a-d251-4d691736ee6f" name="AddAttributeRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="6c20a52a-bab7-e84e-faca-fd79ec5303d9" name="CtorCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c6b3ebb0-58f7-249a-5944-0d31a600f679" name="IsExtensionSupportedNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d5d2c553-97db-8d6c-c984-982299d6091d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="58df167c-cf19-351c-cb09-5c52ae9f97be" name="DeserializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b58e4791-68c0-1bc0-2e48-e1351459ee46" name="UserSetupUrlSetForV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fa52f2db-fc1e-ba31-cc5e-0bcc05998187" name="NoValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0362a92c-a21c-f718-6b1e-3d154c14acd0" name="RequestUsingPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="81f670d0-d314-c53c-9d91-c0765dfc30c1" name="MessagePartsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7bf8e806-68a1-86bc-8d91-9a99d237d35c" name="CreateRequestMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c15c3ab5-e969-efc9-366d-78ebc43ce08f" name="Fetch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a18ae750-318b-bb1f-c2b3-c31da845c085" name="Count" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d5912d3e-441c-a20e-20a2-0b9f0220a762" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="6badbaa8-33d1-13c4-c1f9-aef73a9ac5bf" name="InvalidRawBirthdate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c205832e-711c-62d0-5f5e-78f1250ea7cc" name="AuthenticationTimeUtcSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a4aa113a-57b5-a52c-c4e3-f70d6702badb" name="Default" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e2287de6-cbd2-4298-3fb8-297013749e70" name="SendIndirectMessageFormPostNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="68532d6d-a0cf-5883-17e2-6060707ba9ae" name="DecodeOobToNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a59c5dc0-de4d-8136-8545-2e2e9616de46" name="SerializationWithXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="aedfde98-4357-5b63-7dca-cced838ee416" name="Provider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ae8b7cba-696e-2362-d5e1-79a9c202a994" name="EmptyLineLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e7aacb49-62ef-637d-ada2-0a12d836414d" name="ExtensionFactory" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> @@ -365,90 +367,94 @@ <TestLink id="3772f97f-3fe6-3fc0-350d-4085e7c4329e" name="Test" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fc7af2d7-6262-d761-335b-ef3ec029484d" name="DeserializeVerifyElementOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5dd2e6c9-ff0f-48de-3a1a-cbab61370843" name="SetCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="dbf7855c-0cc6-309f-b5f5-022e0b95fe3b" name="QueryStringLookupWithoutQuery" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="eb932fc7-76c7-b63f-e1e6-a59dea8e4da1" name="AddAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="cc9200bf-1399-d40a-9754-6415f0b7bcf8" name="CreateRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="09b892f2-96e9-45b7-d082-b0bb512c1dd4" name="RequiredNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="40e1121e-8ff3-df73-203b-04baab671a0c" name="ImplicitConversionToStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="536ecd26-4bda-a35e-5af8-666eb9b44940" name="NullValueEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="90f06a50-7b81-05ec-3dc0-7b3e8ade8cfa" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="7fb8d29c-c8ea-7f88-ed42-ae7368d6a429" name="CtorNullStore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="264cd371-e830-c09b-5511-18f54d4c69d5" name="RespondSimpleValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="035cd43a-23d5-af91-12ee-0a0ce78b3548" name="XrdsDiscoveryFromHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f69f1c0c-e258-95fb-4fcb-ad14bfc40e3c" name="Discover" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="01e33554-07cc-ff90-46f8-7d0ca036c9f6" name="ToDictionaryNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e9a5efc6-fde8-8fa4-0bda-2675a4a7e06b" name="DefaultReferenceTypeDeclaredPropertyHasNoKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a260d196-066f-b0ae-a40e-fb9d962b28a4" name="XrdsDirectDiscovery_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a6295302-c78f-4122-ce88-94fc30980262" name="CtorStringNoSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a63c169c-4e9a-bcba-b7cd-c4c5280cd652" name="PrepareMessageForSendingNonExtendableMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a883dc73-d6be-e59a-6da2-0db1d4452679" name="BindingElementsOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="fb0e478e-0f55-b257-75fe-2ab60b57292e" name="SendInvalidMessageTransport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f69f1c0c-e258-95fb-4fcb-ad14bfc40e3c" name="Discover" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="6ef9df5a-d069-0103-5260-593808f232da" name="XrdsDiscoveryFromHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d6b120b7-fc16-6815-927e-af382cd44bbd" name="ReceivedInvalidSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="29e45877-ca7a-85de-5c39-6d43befe1a1e" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="ae1ef27c-fbfe-c57e-a1e0-c1ef9de4ea23" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="384fecbf-f18e-edcb-a2eb-fb0322f031aa" name="ApplyHeadersToResponseNullListenerResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="188ce83b-3117-adb5-4b89-12f2b09be1de" name="CtorSimpleConsumer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="c4001e1c-75ad-236b-284f-318905d2bc3a" name="CreateRequestOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="efd570c9-5e74-17e4-f332-ac257c8e8aff" name="RealmReturnToMismatchV1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="50594141-1a00-b4ab-d794-5b06e67327e5" name="IsTypeUriPresentNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="f17424d2-ed4b-1ea0-a339-733f5092d9d0" name="MaximumAuthenticationAgeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="e9c2087b-1c52-5bb9-bf4e-9046cf281e36" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="924b5295-0d39-5c89-8794-22518091e05a" name="CtorNullToString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a9f7897c-b299-807b-0612-384732cd10c9" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="456c3468-9081-4879-7e7e-8299bd8c7f68" name="IsReadOnly" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="5435ab79-de25-e2fc-0b2d-b05d5686d27d" name="IsUrlWithinRealmTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="dbf7855c-0cc6-309f-b5f5-022e0b95fe3b" name="QueryStringLookupWithoutQuery" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="93041654-1050-3878-6b90-656a7e2e3cfd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="10a8b8e5-e147-838c-0708-be98d5e4490e" name="CtorFull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="599add9e-e9eb-5e8a-ce6b-6dc73c2bb408" name="DataContractNamespace" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="bf73c7f2-33b1-8e18-c4f6-cb8609388754" name="DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="501fa941-c1ac-d4ef-56e7-46827788b571" name="GetRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="d474830d-3636-522c-1564-1b83e7a844d3" name="EmptyLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="78f622a3-750c-12c5-afc6-470c1bf71d85" name="ProtocolDetectionWithoutClues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="1d5fb5a9-e15c-d99c-7a7e-95a4c4d123c2" name="DirectRequestsUsePost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="e8337858-a320-8aad-51aa-402e65a90b75" name="ReplayDetectionTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> - <TestLink id="f20bd439-e277-dc27-4ec4-5d5949d1c6bf" name="RequestUsingAuthorizationHeaderScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> + <TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="83aba528-c8ea-f464-177e-2ea8ae2cfd0b" name="Birthdates" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> <TestLink id="50986611-9de6-a112-2fe8-691210989f45" name="IsTypeUriPresent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" /> </TestLinks> diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs index f535c38..7bd84d9 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs @@ -30,6 +30,11 @@ namespace DotNetOpenAuth.Configuration { private const string OpenIdElementName = "openid"; /// <summary> + /// The name of the <oauth> sub-element. + /// </summary> + private const string OAuthElementName = "oauth"; + + /// <summary> /// Initializes a new instance of the <see cref="DotNetOpenAuthSection"/> class. /// </summary> internal DotNetOpenAuthSection() { @@ -61,5 +66,14 @@ namespace DotNetOpenAuth.Configuration { get { return (OpenIdElement)this[OpenIdElementName] ?? new OpenIdElement(); } set { this[OpenIdElementName] = value; } } + + /// <summary> + /// Gets or sets the configuration for OAuth. + /// </summary> + [ConfigurationProperty(OAuthElementName)] + internal OAuthElement OAuth { + get { return (OAuthElement)this[OAuthElementName] ?? new OAuthElement(); } + set { this[OAuthElementName] = value; } + } } } diff --git a/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs b/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs new file mode 100644 index 0000000..b15c3e3 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthConsumerElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System.Configuration; + + /// <summary> + /// Represents the <oauth/consumer> element in the host's .config file. + /// </summary> + internal class OAuthConsumerElement : ConfigurationElement { + /// <summary> + /// Gets the name of the security sub-element. + /// </summary> + private const string SecuritySettingsConfigName = "security"; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthConsumerElement"/> class. + /// </summary> + internal OAuthConsumerElement() { + } + + /// <summary> + /// Gets or sets the security settings. + /// </summary> + [ConfigurationProperty(SecuritySettingsConfigName)] + public OAuthConsumerSecuritySettingsElement SecuritySettings { + get { return (OAuthConsumerSecuritySettingsElement)this[SecuritySettingsConfigName] ?? new OAuthConsumerSecuritySettingsElement(); } + set { this[SecuritySettingsConfigName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs new file mode 100644 index 0000000..5e75390 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthConsumerSecuritySettingsElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth; + + /// <summary> + /// Security settings that are applicable to consumers. + /// </summary> + internal class OAuthConsumerSecuritySettingsElement : ConfigurationElement { + /// <summary> + /// Initializes a new instance of the <see cref="OAuthConsumerSecuritySettingsElement"/> class. + /// </summary> + internal OAuthConsumerSecuritySettingsElement() { + } + + /// <summary> + /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. + /// </summary> + /// <returns>The newly created security settings object.</returns> + internal ConsumerSecuritySettings CreateSecuritySettings() { + return new ConsumerSecuritySettings(); + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/OAuthElement.cs b/src/DotNetOpenAuth/Configuration/OAuthElement.cs new file mode 100644 index 0000000..282bdba --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/OAuthElement.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System.Configuration; + + /// <summary> + /// Represents the <oauth> element in the host's .config file. + /// </summary> + internal class OAuthElement : ConfigurationElement { + /// <summary> + /// The name of the <consumer> sub-element. + /// </summary> + private const string ConsumerElementName = "consumer"; + + /// <summary> + /// The name of the <serviceProvider> sub-element. + /// </summary> + private const string ServiceProviderElementName = "serviceProvider"; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthElement"/> class. + /// </summary> + internal OAuthElement() { + } + + /// <summary> + /// Gets or sets the configuration specific for Consumers. + /// </summary> + [ConfigurationProperty(ConsumerElementName)] + internal OAuthConsumerElement Consumer { + get { return (OAuthConsumerElement)this[ConsumerElementName] ?? new OAuthConsumerElement(); } + set { this[ConsumerElementName] = value; } + } + + /// <summary> + /// Gets or sets the configuration specific for Service Providers. + /// </summary> + [ConfigurationProperty(ServiceProviderElementName)] + internal OAuthServiceProviderElement ServiceProvider { + get { return (OAuthServiceProviderElement)this[ServiceProviderElementName] ?? new OAuthServiceProviderElement(); } + set { this[ServiceProviderElementName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs new file mode 100644 index 0000000..5ff528d --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthServiceProviderElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System.Configuration; + + /// <summary> + /// Represents the <oauth/serviceProvider> element in the host's .config file. + /// </summary> + internal class OAuthServiceProviderElement : ConfigurationElement { + /// <summary> + /// Gets the name of the security sub-element. + /// </summary> + private const string SecuritySettingsConfigName = "security"; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthServiceProviderElement"/> class. + /// </summary> + internal OAuthServiceProviderElement() { + } + + /// <summary> + /// Gets or sets the security settings. + /// </summary> + [ConfigurationProperty(SecuritySettingsConfigName)] + public OAuthServiceProviderSecuritySettingsElement SecuritySettings { + get { return (OAuthServiceProviderSecuritySettingsElement)this[SecuritySettingsConfigName] ?? new OAuthServiceProviderSecuritySettingsElement(); } + set { this[SecuritySettingsConfigName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs new file mode 100644 index 0000000..c58c023 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthServiceProviderSecuritySettingsElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth; + + /// <summary> + /// Security settings that are applicable to service providers. + /// </summary> + internal class OAuthServiceProviderSecuritySettingsElement : ConfigurationElement { + /// <summary> + /// Gets the name of the @minimumRequiredOAuthVersion attribute. + /// </summary> + private const string MinimumRequiredOAuthVersionConfigName = "minimumRequiredOAuthVersion"; + + /// <summary> + /// Gets the name of the @maxAuthorizationTime attribute. + /// </summary> + private const string MaximumRequestTokenTimeToLiveConfigName = "maxAuthorizationTime"; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthServiceProviderSecuritySettingsElement"/> class. + /// </summary> + internal OAuthServiceProviderSecuritySettingsElement() { + } + + /// <summary> + /// Gets or sets the minimum OAuth version a Consumer is required to support in order for this library to interoperate with it. + /// </summary> + /// <remarks> + /// Although the earliest versions of OAuth are supported, for security reasons it may be desirable to require the + /// remote party to support a later version of OAuth. + /// </remarks> + [ConfigurationProperty(MinimumRequiredOAuthVersionConfigName, DefaultValue = "V10")] + public ProtocolVersion MinimumRequiredOAuthVersion { + get { return (ProtocolVersion)this[MinimumRequiredOAuthVersionConfigName]; } + set { this[MinimumRequiredOAuthVersionConfigName] = value; } + } + + /// <summary> + /// Gets or sets the maximum time a user can take to complete authorization. + /// </summary> + /// <remarks> + /// This time limit serves as a security mitigation against brute force attacks to + /// compromise (unauthorized or authorized) request tokens. + /// Longer time limits is more friendly to slow users or consumers, while shorter + /// time limits provide better security. + /// </remarks> + [ConfigurationProperty(MaximumRequestTokenTimeToLiveConfigName, DefaultValue = "0:05")] // 5 minutes + [PositiveTimeSpanValidator] + public TimeSpan MaximumRequestTokenTimeToLive { + get { return (TimeSpan)this[MaximumRequestTokenTimeToLiveConfigName]; } + set { this[MaximumRequestTokenTimeToLiveConfigName] = value; } + } + + /// <summary> + /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. + /// </summary> + /// <returns>The newly created security settings object.</returns> + internal ServiceProviderSecuritySettings CreateSecuritySettings() { + return new ServiceProviderSecuritySettings { + MinimumRequiredOAuthVersion = this.MinimumRequiredOAuthVersion, + }; + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs index 25e067e..0d936b2 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs @@ -32,11 +32,16 @@ namespace DotNetOpenAuth.Configuration { private const string ExtensionFactoriesElementName = "extensionFactories"; /// <summary> - /// Gets the name of the @maxAuthenticationTime attribute. + /// The name of the @maxAuthenticationTime attribute. /// </summary> private const string MaxAuthenticationTimePropertyName = "maxAuthenticationTime"; /// <summary> + /// The name of the @cacheDiscovery attribute. + /// </summary> + private const string CacheDiscoveryPropertyName = "cacheDiscovery"; + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdElement"/> class. /// </summary> internal OpenIdElement() { @@ -58,6 +63,24 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> + /// Gets or sets a value indicating whether the results of Identifier discovery + /// should be cached. + /// </summary> + /// <value> + /// Use <c>true</c> to allow identifier discovery to immediately return cached results when available; + /// otherwise, use <c>false</c>.to force fresh results every time at the cost of slightly slower logins. + /// The default value is <c>true</c>. + /// </value> + /// <remarks> + /// When enabled, caching is done according to HTTP standards. + /// </remarks> + [ConfigurationProperty(CacheDiscoveryPropertyName, DefaultValue = true)] + internal bool CacheDiscovery { + get { return (bool)this[CacheDiscoveryPropertyName]; } + set { this[CacheDiscoveryPropertyName] = value; } + } + + /// <summary> /// Gets or sets the configuration specific for Relying Parties. /// </summary> [ConfigurationProperty(RelyingPartyElementName)] diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index a6069b4..64519f6 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -177,6 +177,11 @@ <Compile Include="Configuration\AssociationTypeElement.cs" /> <Compile Include="Configuration\DotNetOpenAuthSection.cs" /> <Compile Include="Configuration\MessagingElement.cs" /> + <Compile Include="Configuration\OAuthConsumerElement.cs" /> + <Compile Include="Configuration\OAuthConsumerSecuritySettingsElement.cs" /> + <Compile Include="Configuration\OAuthElement.cs" /> + <Compile Include="Configuration\OAuthServiceProviderElement.cs" /> + <Compile Include="Configuration\OAuthServiceProviderSecuritySettingsElement.cs" /> <Compile Include="Configuration\OpenIdElement.cs" /> <Compile Include="Configuration\OpenIdProviderElement.cs" /> <Compile Include="Configuration\OpenIdProviderSecuritySettingsElement.cs" /> @@ -222,9 +227,10 @@ <Compile Include="Messaging\NetworkDirectWebResponse.cs" /> <Compile Include="Messaging\OutgoingWebResponseActionResult.cs" /> <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" /> + <Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" /> <Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" /> <Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" /> - <Compile Include="OAuth\ChannelElements\IConsumerCertificateProvider.cs" /> + <Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" /> <Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\IOpenIdOAuthTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\IServiceProviderTokenManager.cs" /> @@ -234,11 +240,15 @@ <Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" /> <Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" /> <Compile Include="OAuth\ChannelElements\HmacSha1SigningBindingElement.cs" /> + <Compile Include="OAuth\ChannelElements\IServiceProviderRequestToken.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementBaseContract.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" /> <Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" /> <Compile Include="OAuth\ChannelElements\TokenType.cs" /> + <Compile Include="OAuth\ChannelElements\UriOrOobEncoding.cs" /> + <Compile Include="OAuth\ChannelElements\TokenHandlingBindingElement.cs" /> <Compile Include="OAuth\ConsumerBase.cs" /> + <Compile Include="OAuth\ConsumerSecuritySettings.cs" /> <Compile Include="OAuth\DesktopConsumer.cs" /> <Compile Include="GlobalSuppressions.cs" /> <Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" /> @@ -249,11 +259,14 @@ <DesignTime>True</DesignTime> <DependentUpon>OAuthStrings.resx</DependentUpon> </Compile> + <Compile Include="OAuth\SecuritySettings.cs" /> <Compile Include="OAuth\ServiceProviderDescription.cs" /> <Compile Include="OAuth\Messages\ITokenContainingMessage.cs" /> <Compile Include="OAuth\Messages\SignedMessageBase.cs" /> <Compile Include="Messaging\Bindings\NonceMemoryStore.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementBase.cs" /> + <Compile Include="OAuth\ServiceProviderSecuritySettings.cs" /> + <Compile Include="OAuth\VerificationCodeFormat.cs" /> <Compile Include="OAuth\WebConsumer.cs" /> <Compile Include="Messaging\IDirectWebRequestHandler.cs" /> <Compile Include="OAuth\ChannelElements\ITamperResistantOAuthMessage.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs index bb2b28a..c8d5873 100644 --- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs +++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs @@ -28,11 +28,6 @@ namespace DotNetOpenAuth.Messaging.Bindings { private int nonceLength = 8; /// <summary> - /// A random number generator. - /// </summary> - private Random generator = new Random(); - - /// <summary> /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class. /// </summary> /// <param name="nonceStore">The store where nonces will be persisted and checked.</param> @@ -145,11 +140,7 @@ namespace DotNetOpenAuth.Messaging.Bindings { /// </summary> /// <returns>The nonce string.</returns> private string GenerateUniqueFragment() { - char[] nonce = new char[this.nonceLength]; - for (int i = 0; i < nonce.Length; i++) { - nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)]; - } - return new string(nonce); + return MessagingUtilities.GetRandomString(this.nonceLength, AllowedCharacters); } } } diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index d039f9b..0f307b5 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -28,6 +28,12 @@ namespace DotNetOpenAuth.Messaging { [ContractClass(typeof(ChannelContract))] public abstract class Channel : IDisposable { /// <summary> + /// The content-type used on HTTP POST requests where the POST entity is a + /// URL-encoded series of key=value pairs. + /// </summary> + protected internal const string HttpFormUrlEncoded = "application/x-www-form-urlencoded"; + + /// <summary> /// The encoding to use when writing out POST entity strings. /// </summary> private static readonly Encoding PostEntityEncoding = new UTF8Encoding(false); @@ -317,10 +323,10 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Gets the protocol message embedded in the given HTTP request, if present. + /// Gets the protocol message embedded in the current HTTP request. /// </summary> /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam> - /// <returns>The deserialized message.</returns> + /// <returns>The deserialized message. Never null.</returns> /// <remarks> /// Requires an HttpContext.Current context. /// </remarks> @@ -333,11 +339,11 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Gets the protocol message that may be embedded in the given HTTP request. + /// Gets the protocol message embedded in the given HTTP request. /// </summary> /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam> /// <param name="httpRequest">The request to search for an embedded message.</param> - /// <returns>The deserialized message, if one is found. Null otherwise.</returns> + /// <returns>The deserialized message. Never null.</returns> /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception> [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")] public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest) @@ -848,7 +854,7 @@ namespace DotNetOpenAuth.Messaging { ErrorUtilities.VerifyArgumentNotNull(httpRequest, "httpRequest"); ErrorUtilities.VerifyArgumentNotNull(fields, "fields"); - httpRequest.ContentType = "application/x-www-form-urlencoded"; + httpRequest.ContentType = HttpFormUrlEncoded; // Setting the content-encoding to "utf-8" causes Google to reply // with a 415 UnsupportedMediaType. But adding it doesn't buy us diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs index d5b2040..d81ce17 100644 --- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs @@ -35,10 +35,18 @@ namespace DotNetOpenAuth.Messaging { /// Throws an internal error exception. /// </summary> /// <param name="errorMessage">The error message.</param> + /// <returns>Nothing. But included here so callers can "throw" this method for C# safety.</returns> /// <exception cref="InternalErrorException">Always thrown.</exception> [Pure] - internal static void ThrowInternal(string errorMessage) { - VerifyInternal(false, errorMessage); + internal static Exception ThrowInternal(string errorMessage) { + // Since internal errors are really bad, take this chance to + // help the developer find the cause by breaking into the + // debugger if one is attached. + if (Debugger.IsAttached) { + Debugger.Break(); + } + + throw new InternalErrorException(errorMessage); } /// <summary> @@ -52,14 +60,7 @@ namespace DotNetOpenAuth.Messaging { Contract.Ensures(condition); Contract.EnsuresOnThrow<InternalErrorException>(!condition); if (!condition) { - // Since internal errors are really bad, take this chance to - // help the developer find the cause by breaking into the - // debugger if one is attached. - if (Debugger.IsAttached) { - Debugger.Break(); - } - - throw new InternalErrorException(errorMessage); + ThrowInternal(errorMessage); } } @@ -202,7 +203,17 @@ namespace DotNetOpenAuth.Messaging { Contract.EnsuresOnThrow<ProtocolException>(!condition); Contract.Assume(message != null); if (!condition) { - throw new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args)); + var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args)); + if (Logger.Messaging.IsErrorEnabled) { + Logger.Messaging.Error( + string.Format( + CultureInfo.CurrentCulture, + "Protocol error: {0}{1}{2}", + exception.Message, + Environment.NewLine, + new StackTrace())); + } + throw exception; } } diff --git a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs index 309bad3..cbbe28e 100644 --- a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs +++ b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs @@ -26,7 +26,7 @@ namespace DotNetOpenAuth.Messaging { AuthorizationHeaderRequest = 0x1, /// <summary> - /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded. + /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded. /// </summary> PostRequest = 0x2, diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs index 5908779..6cb27e6 100644 --- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs @@ -225,7 +225,7 @@ namespace DotNetOpenAuth.Messaging { get { Contract.Ensures(Contract.Result<NameValueCollection>() != null); if (this.form == null) { - if (this.HttpMethod == "POST" && this.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded") { + if (this.HttpMethod == "POST" && this.Headers[HttpRequestHeader.ContentType] == Channel.HttpFormUrlEncoded) { StreamReader reader = new StreamReader(this.InputStream); long originalPosition = 0; if (this.InputStream.CanSeek) { diff --git a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs index 82910d5..22c660c 100644 --- a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs +++ b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.Messaging { using System; + using System.Diagnostics; using System.Net.Security; using System.Reflection; @@ -13,6 +14,7 @@ namespace DotNetOpenAuth.Messaging { /// Applied to fields and properties that form a key/value in a protocol message. /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = true)] + [DebuggerDisplay("MessagePartAttribute {Name}")] public sealed class MessagePartAttribute : Attribute { /// <summary> /// The overridden name to use as the serialized name for the property. diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 4cfc46a..da95771 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -31,6 +31,32 @@ namespace DotNetOpenAuth.Messaging { internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider(); /// <summary> + /// A pseudo-random data generator (NOT cryptographically strong random data) + /// </summary> + internal static readonly Random NonCryptoRandomDataGenerator = new Random(); + + /// <summary> + /// The uppercase alphabet. + /// </summary> + internal const string UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /// <summary> + /// The lowercase alphabet. + /// </summary> + internal const string LowercaseLetters = "abcdefghijklmnopqrstuvwxyz"; + + /// <summary> + /// The set of base 10 digits. + /// </summary> + internal const string Digits = "0123456789"; + + /// <summary> + /// The set of digits, and alphabetic letters (upper and lowercase) that are clearly + /// visually distinguishable. + /// </summary> + internal const string AlphaNumericNoLookAlikes = "23456789abcdefghjkmnpqrstwxyzABCDEFGHJKMNPQRSTWXYZ"; + + /// <summary> /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. /// </summary> private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; @@ -163,6 +189,24 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets a random string made up of a given set of allowable characters. + /// </summary> + /// <param name="length">The length of the desired random string.</param> + /// <param name="allowableCharacters">The allowable characters.</param> + /// <returns>A random string.</returns> + internal static string GetRandomString(int length, string allowableCharacters) { + Contract.Requires(length >= 0); + Contract.Requires(allowableCharacters != null && allowableCharacters.Length >= 2); + + char[] randomString = new char[length]; + for (int i = 0; i < length; i++) { + randomString[i] = allowableCharacters[NonCryptoRandomDataGenerator.Next(allowableCharacters.Length)]; + } + + return new string(randomString); + } + + /// <summary> /// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance, /// taking care to set some headers to the appropriate properties of /// <see cref="HttpResponse" /> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs new file mode 100644 index 0000000..7581550 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// <copyright file="IMessagePartNullEncoder.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging.Reflection { + /// <summary> + /// A message part encoder that has a special encoding for a null value. + /// </summary> + public interface IMessagePartNullEncoder : IMessagePartEncoder { + /// <summary> + /// Gets the string representation to include in a serialized message + /// when the message part has a <c>null</c> value. + /// </summary> + string EncodedNullValue { get; } + } +} diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 0e26860..0732bb2 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net.Security; @@ -17,6 +18,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <summary> /// Describes an individual member of a message and assists in its serialization. /// </summary> + [DebuggerDisplay("MessagePart {Name}")] internal class MessagePart { /// <summary> /// A map of converters that help serialize custom objects to string values and back again. @@ -113,10 +115,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); } } else { - var encoder = GetEncoder(attribute.Encoder); - this.converter = new ValueMapping( - obj => encoder.Encode(obj), - str => encoder.Decode(str)); + this.converter = new ValueMapping(GetEncoder(attribute.Encoder)); } // readonly and const fields are considered legal, and "constants" for message transport. @@ -286,7 +285,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// An instance of the appropriate type for setting the member. /// </returns> private object ToValue(string value) { - return value == null ? null : this.converter.StringToValue(value); + return this.converter.StringToValue(value); } /// <summary> @@ -297,7 +296,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// The string representation of the member's value. /// </returns> private string ToString(object value) { - return value == null ? null : this.converter.ValueToString(value); + return this.converter.ValueToString(value); } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs index bdc2d7f..332274e 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs @@ -14,12 +14,12 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <summary> /// The mapping function that converts some custom type to a string. /// </summary> - internal Func<object, string> ValueToString; + internal readonly Func<object, string> ValueToString; /// <summary> /// The mapping function that converts a string to some custom type. /// </summary> - internal Func<string, object> StringToValue; + internal readonly Func<string, object> StringToValue; /// <summary> /// Initializes a new instance of the <see cref="ValueMapping"/> struct. @@ -27,16 +27,24 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <param name="toString">The mapping function that converts some custom type to a string.</param> /// <param name="toValue">The mapping function that converts a string to some custom type.</param> internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) { - if (toString == null) { - throw new ArgumentNullException("toString"); - } - - if (toValue == null) { - throw new ArgumentNullException("toValue"); - } + ErrorUtilities.VerifyArgumentNotNull(toString, "toString"); + ErrorUtilities.VerifyArgumentNotNull(toValue, "toValue"); this.ValueToString = toString; this.StringToValue = toValue; } + + /// <summary> + /// Initializes a new instance of the <see cref="ValueMapping"/> struct. + /// </summary> + /// <param name="encoder">The encoder.</param> + internal ValueMapping(IMessagePartEncoder encoder) { + ErrorUtilities.VerifyArgumentNotNull(encoder, "encoder"); + var nullEncoder = encoder as IMessagePartNullEncoder; + string nullString = nullEncoder != null ? nullEncoder.EncodedNullValue : null; + + this.ValueToString = obj => (obj != null) ? encoder.Encode(obj) : nullString; + this.StringToValue = str => (str != null) ? encoder.Decode(str) : null; + } } } diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs index e8d8fe1..cc991cd 100644 --- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs @@ -140,6 +140,7 @@ namespace DotNetOpenAuth.Messaging { // We don't want to blindly set all ServicePoints to not use the Expect header // as that would be a security hole allowing any visitor to a web site change // the web site's global behavior when calling that host. + Logger.Http.InfoFormat("HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time.", request.RequestUri); request.ServicePoint.Expect100Continue = false; // TODO: investigate that CAS may throw here // An alternative to ServicePoint if we don't have permission to set that, @@ -179,6 +180,31 @@ namespace DotNetOpenAuth.Messaging { #endregion /// <summary> + /// Determines whether an exception was thrown because of the remote HTTP server returning HTTP 417 Expectation Failed. + /// </summary> + /// <param name="ex">The caught exception.</param> + /// <returns> + /// <c>true</c> if the failure was originally caused by a 417 Exceptation Failed error; otherwise, <c>false</c>. + /// </returns> + internal static bool IsExceptionFrom417ExpectationFailed(Exception ex) { + while (ex != null) { + WebException webEx = ex as WebException; + if (webEx != null) { + HttpWebResponse response = webEx.Response as HttpWebResponse; + if (response != null) { + if (response.StatusCode == HttpStatusCode.ExpectationFailed) { + return true; + } + } + } + + ex = ex.InnerException; + } + + return false; + } + + /// <summary> /// Initiates a POST request and prepares for sending data. /// </summary> /// <param name="request">The HTTP request with information about the remote party to contact.</param> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs deleted file mode 100644 index 7e6ae54..0000000 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="IConsumerCertificateProvider.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth.ChannelElements { - using System.Security.Cryptography.X509Certificates; - - /// <summary> - /// A provider that hosts can implement to hook up their RSA-SHA1 binding elements - /// to their list of known Consumers' certificates. - /// </summary> - public interface IConsumerCertificateProvider { - /// <summary> - /// Gets the certificate that can be used to verify the signature of an incoming - /// message from a Consumer. - /// </summary> - /// <param name="consumerMessage">The incoming message from some Consumer.</param> - /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns> - X509Certificate2 GetCertificate(ITamperResistantOAuthMessage consumerMessage); - } -} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs new file mode 100644 index 0000000..db505d5 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// <copyright file="IConsumerDescription.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Security.Cryptography.X509Certificates; + + /// <summary> + /// A description of a consumer from a Service Provider's point of view. + /// </summary> + public interface IConsumerDescription { + /// <summary> + /// Gets the Consumer key. + /// </summary> + string Key { get; } + + /// <summary> + /// Gets the consumer secret. + /// </summary> + string Secret { get; } + + /// <summary> + /// Gets the certificate that can be used to verify the signature of an incoming + /// message from a Consumer. + /// </summary> + /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns> + /// <remarks> + /// This property must be implemented only if the RSA-SHA1 algorithm is supported by the Service Provider. + /// </remarks> + X509Certificate2 Certificate { get; } + + /// <summary> + /// Gets the callback URI that this consumer has pre-registered with the service provider, if any. + /// </summary> + /// <value>A URI that user authorization responses should be directed to; or <c>null</c> if no preregistered callback was arranged.</value> + Uri Callback { get; } + + /// <summary> + /// Gets the verification code format that is most appropriate for this consumer + /// when a callback URI is not available. + /// </summary> + /// <value>A set of characters that can be easily keyed in by the user given the Consumer's + /// application type and form factor.</value> + /// <remarks> + /// The value <see cref="OAuth.VerificationCodeFormat.IncludedInCallback"/> should NEVER be returned + /// since this property is only used in no callback scenarios anyway. + /// </remarks> + VerificationCodeFormat VerificationCodeFormat { get; } + + /// <summary> + /// Gets the length of the verification code to issue for this Consumer. + /// </summary> + /// <value>A positive number, generally at least 4.</value> + int VerificationCodeLength { get; } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs new file mode 100644 index 0000000..6dfa416 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------- +// <copyright file="IServiceProviderRequestToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// A description of a request token and its metadata as required by a Service Provider + /// </summary> + public interface IServiceProviderRequestToken { + /// <summary> + /// Gets the token itself. + /// </summary> + string Token { get; } + + /// <summary> + /// Gets the consumer key that requested this token. + /// </summary> + string ConsumerKey { get; } + + /// <summary> + /// Gets the (local) date that this request token was first created on. + /// </summary> + DateTime CreatedOn { get; } + + /// <summary> + /// Gets or sets the callback associated specifically with this token, if any. + /// </summary> + /// <value>The callback URI; or <c>null</c> if no callback was specifically assigned to this token.</value> + Uri Callback { get; set; } + + /// <summary> + /// Gets or sets the verifier that the consumer must include in the <see cref="AuthorizedTokenRequest"/> + /// message to exchange this request token for an access token. + /// </summary> + /// <value>The verifier code, or <c>null</c> if none has been assigned (yet).</value> + string VerificationCode { get; set; } + + /// <summary> + /// Gets or sets the version of the Consumer that requested this token. + /// </summary> + /// <remarks> + /// This property is used to determine whether a <see cref="VerificationCode"/> must be + /// generated when the user authorizes the Consumer or not. + /// </remarks> + Version ConsumerVersion { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs index e1c1e3f..fa008ac 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs @@ -16,12 +16,19 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> public interface IServiceProviderTokenManager : ITokenManager { /// <summary> - /// Gets the Consumer Secret for a given a Consumer Key. + /// Gets the Consumer description for a given a Consumer Key. /// </summary> /// <param name="consumerKey">The Consumer Key.</param> - /// <returns>The Consumer Secret.</returns> - /// <exception cref="ArgumentException">Thrown if the consumer key cannot be found.</exception> - /// <exception cref="InvalidOperationException">May be thrown if called when the signature algorithm does not require a consumer secret, such as when RSA-SHA1 is used.</exception> - string GetConsumerSecret(string consumerKey); + /// <returns>A description of the consumer. Never null.</returns> + /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception> + IConsumerDescription GetConsumer(string consumerKey); + + /// <summary> + /// Gets details on the named request token. + /// </summary> + /// <param name="token">The request token.</param> + /// <returns>A description of the token. Never null.</returns> + /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception> + IServiceProviderRequestToken GetRequestToken(string token); } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 3243e47..123582a 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <see cref="OAuthConsumerMessageFactory"/> or <see cref="OAuthServiceProviderMessageFactory"/>. /// </param> internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, IMessageFactory messageTypeProvider) - : base(messageTypeProvider, new OAuthHttpMethodBindingElement(), signingBindingElement, new StandardExpirationBindingElement(), new StandardReplayProtectionBindingElement(store)) { + : base(messageTypeProvider, InitializeBindingElements(signingBindingElement, store, tokenManager)) { ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); this.TokenManager = tokenManager; @@ -122,7 +122,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { string authorization = request.Headers[HttpRequestHeader.Authorization]; if (authorization != null) { string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter? - string oauthPrefix = Protocol.Default.AuthorizationHeaderScheme + " "; + string oauthPrefix = Protocol.AuthorizationHeaderScheme + " "; // The Authorization header may have multiple uses, and OAuth may be just one of them. // Go through each one looking for an OAuth one. @@ -142,8 +142,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } // Scrape the entity - foreach (string key in request.Form) { - fields.Add(key, request.Form[key]); + if (string.Equals(request.Headers[HttpRequestHeader.ContentType], HttpFormUrlEncoded, StringComparison.Ordinal)) { + foreach (string key in request.Form) { + fields.Add(key, request.Form[key]); + } } // Scrape the query string @@ -239,6 +241,29 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } /// <summary> + /// Initializes the binding elements for the OAuth channel. + /// </summary> + /// <param name="signingBindingElement">The signing binding element.</param> + /// <param name="store">The nonce store.</param> + /// <param name="tokenManager">The token manager.</param> + /// <returns>An array of binding elements used to initialize the channel.</returns> + private static IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager) { + var bindingElements = new List<IChannelBindingElement> { + new OAuthHttpMethodBindingElement(), + signingBindingElement, + new StandardExpirationBindingElement(), + new StandardReplayProtectionBindingElement(store), + }; + + var spTokenManager = tokenManager as IServiceProviderTokenManager; + if (spTokenManager != null) { + bindingElements.Insert(0, new TokenHandlingBindingElement(spTokenManager)); + } + + return bindingElements.ToArray(); + } + + /// <summary> /// Uri-escapes the names and values in a dictionary per OAuth 1.0 section 5.1. /// </summary> /// <param name="source">The dictionary with names and values to encode.</param> @@ -305,7 +330,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { httpRequest.Method = GetHttpMethod(requestMessage); StringBuilder authorization = new StringBuilder(); - authorization.Append(protocol.AuthorizationHeaderScheme); + authorization.Append(Protocol.AuthorizationHeaderScheme); authorization.Append(" "); foreach (var pair in fields) { string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); @@ -364,7 +389,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { ErrorUtilities.VerifyInternal(consumerKey == consumerTokenManager.ConsumerKey, "The token manager consumer key and the consumer key set earlier do not match!"); return consumerTokenManager.ConsumerSecret; } else { - return ((IServiceProviderTokenManager)this.TokenManager).GetConsumerSecret(consumerKey); + return ((IServiceProviderTokenManager)this.TokenManager).GetConsumer(consumerKey).Secret; } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs index fce351b..e05bb62 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs @@ -44,7 +44,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { MessageBase message = null; if (fields.ContainsKey("oauth_token")) { - message = new UserAuthorizationResponse(recipient.Location); + Protocol protocol = fields.ContainsKey("oauth_verifier") ? Protocol.V10a : Protocol.V10; + message = new UserAuthorizationResponse(recipient.Location, protocol.Version); } if (message != null) { @@ -92,7 +93,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var unauthorizedTokenRequest = request as UnauthorizedTokenRequest; var authorizedTokenRequest = request as AuthorizedTokenRequest; if (unauthorizedTokenRequest != null) { - message = new UnauthorizedTokenResponse(unauthorizedTokenRequest); + Protocol protocol = fields.ContainsKey("oauth_callback_confirmed") ? Protocol.V10a : Protocol.V10; + message = new UnauthorizedTokenResponse(unauthorizedTokenRequest, protocol.Version); } else if (authorizedTokenRequest != null) { message = new AuthorizedTokenResponse(authorizedTokenRequest); } else { diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs index 1aaea7f..4727a6d 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs @@ -54,29 +54,51 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { ErrorUtilities.VerifyArgumentNotNull(fields, "fields"); MessageBase message = null; + Protocol protocol = Protocol.V10; // default to assuming the less-secure 1.0 instead of 1.0a until we prove otherwise. + string token; + fields.TryGetValue("oauth_token", out token); - if (fields.ContainsKey("oauth_consumer_key") && - !fields.ContainsKey("oauth_token")) { - message = new UnauthorizedTokenRequest(recipient); - } else if (fields.ContainsKey("oauth_consumer_key") && - fields.ContainsKey("oauth_token")) { - // Discern between RequestAccessToken and AccessProtectedResources, - // which have all the same parameters, by figuring out what type of token - // is in the token parameter. - bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(fields["oauth_token"]) == TokenType.AccessToken; + try { + if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) { + protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10; + message = new UnauthorizedTokenRequest(recipient, protocol.Version); + } else if (fields.ContainsKey("oauth_consumer_key") && fields.ContainsKey("oauth_token")) { + // Discern between RequestAccessToken and AccessProtectedResources, + // which have all the same parameters, by figuring out what type of token + // is in the token parameter. + bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(token) == TokenType.AccessToken; - message = tokenTypeIsAccessToken ? (MessageBase)new AccessProtectedResourceRequest(recipient) : - new AuthorizedTokenRequest(recipient); - } else { - // fail over to the message with no required fields at all. - message = new UserAuthorizationRequest(recipient); - } + if (tokenTypeIsAccessToken) { + message = (MessageBase)new AccessProtectedResourceRequest(recipient, protocol.Version); + } else { + // Discern between 1.0 and 1.0a requests by checking on the consumer version we stored + // when the consumer first requested an unauthorized token. + protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion); + message = new AuthorizedTokenRequest(recipient, protocol.Version); + } + } else { + // fail over to the message with no required fields at all. + if (token != null) { + protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion); + } - if (message != null) { - message.SetAsIncoming(); - } + // If a callback parameter is included, that suggests either the consumer + // is following OAuth 1.0 instead of 1.0a, or that a hijacker is trying + // to attack. Either way, if the consumer started out as a 1.0a, keep it + // that way, and we'll just ignore the oauth_callback included in this message + // by virtue of the UserAuthorizationRequest message not including it in its + // 1.0a payload. + message = new UserAuthorizationRequest(recipient, protocol.Version); + } - return message; + if (message != null) { + message.SetAsIncoming(); + } + + return message; + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound); + } } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs index 779f2c5..4f8b5e5 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System; + using System.Diagnostics.Contracts; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -16,15 +17,24 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> public class RsaSha1SigningBindingElement : SigningBindingElementBase { /// <summary> + /// The name of the hash algorithm to use. + /// </summary> + private const string HashAlgorithmName = "RSA-SHA1"; + + /// <summary> + /// The token manager for the service provider. + /// </summary> + private IServiceProviderTokenManager tokenManager; + + /// <summary> /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class /// for use by Consumers. /// </summary> /// <param name="signingCertificate">The certificate used to sign outgoing messages.</param> public RsaSha1SigningBindingElement(X509Certificate2 signingCertificate) - : this() { - if (signingCertificate == null) { - throw new ArgumentNullException("signingCertificate"); - } + : base(HashAlgorithmName) { + Contract.Requires(signingCertificate != null); + ErrorUtilities.VerifyArgumentNotNull(signingCertificate, "signingCertificate"); this.SigningCertificate = signingCertificate; } @@ -33,21 +43,21 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class /// for use by Service Providers. /// </summary> - public RsaSha1SigningBindingElement() - : base("RSA-SHA1") { + /// <param name="tokenManager">The token manager.</param> + public RsaSha1SigningBindingElement(IServiceProviderTokenManager tokenManager) + : base(HashAlgorithmName) { + Contract.Requires(tokenManager != null); + ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); + + this.tokenManager = tokenManager; } /// <summary> - /// Gets or sets the certificate used to sign outgoing messages. + /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers. /// </summary> public X509Certificate2 SigningCertificate { get; set; } /// <summary> - /// Gets or sets the consumer certificate provider. - /// </summary> - public IConsumerCertificateProvider ConsumerCertificateProvider { get; set; } - - /// <summary> /// Calculates a signature for a given message. /// </summary> /// <param name="message">The message to sign.</param> @@ -56,13 +66,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// This method signs the message per OAuth 1.0 section 9.3. /// </remarks> protected override string GetSignature(ITamperResistantOAuthMessage message) { - if (message == null) { - throw new ArgumentNullException("message"); - } - - if (this.SigningCertificate == null) { - throw new InvalidOperationException(OAuthStrings.X509CertificateNotProvidedForSigning); - } + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); @@ -80,16 +85,14 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>. /// </returns> protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) { - if (this.ConsumerCertificateProvider == null) { - throw new InvalidOperationException(OAuthStrings.ConsumerCertificateProviderNotAvailable); - } + ErrorUtilities.VerifyInternal(this.tokenManager != null, "No token manager available for fetching Consumer public certificates."); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); byte[] carriedSignature = Convert.FromBase64String(message.Signature); - X509Certificate2 cert = this.ConsumerCertificateProvider.GetCertificate(message); + X509Certificate2 cert = this.tokenManager.GetConsumer(message.ConsumerKey).Certificate; if (cert == null) { Logger.Signatures.WarnFormat("Incoming message from consumer '{0}' could not be matched with an appropriate X.509 certificate for signature verification.", message.ConsumerKey); return false; @@ -105,10 +108,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <returns>A new instance of the binding element.</returns> protected override ITamperProtectionChannelBindingElement Clone() { - return new RsaSha1SigningBindingElement() { - ConsumerCertificateProvider = this.ConsumerCertificateProvider, - SigningCertificate = this.SigningCertificate, - }; + if (this.tokenManager != null) { + return new RsaSha1SigningBindingElement(this.tokenManager); + } else { + return new RsaSha1SigningBindingElement(this.SigningCertificate); + } } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs new file mode 100644 index 0000000..e8acdcf --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------- +// <copyright file="TokenHandlingBindingElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// A binding element for Service Providers to manage the + /// callbacks and verification codes on applicable messages. + /// </summary> + internal class TokenHandlingBindingElement : IChannelBindingElement { + /// <summary> + /// The token manager offered by the service provider. + /// </summary> + private IServiceProviderTokenManager tokenManager; + + /// <summary> + /// Initializes a new instance of the <see cref="TokenHandlingBindingElement"/> class. + /// </summary> + /// <param name="tokenManager">The token manager.</param> + internal TokenHandlingBindingElement(IServiceProviderTokenManager tokenManager) { + Contract.Requires(tokenManager != null); + ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); + + this.tokenManager = tokenManager; + } + + #region IChannelBindingElement Members + + /// <summary> + /// Gets or sets the channel that this binding element belongs to. + /// </summary> + /// <remarks> + /// This property is set by the channel when it is first constructed. + /// </remarks> + public Channel Channel { get; set; } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public MessageProtections Protection { + get { return MessageProtections.None; } + } + + /// <summary> + /// Prepares a message for sending based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The message to prepare for sending.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + + var userAuthResponse = message as UserAuthorizationResponse; + if (userAuthResponse != null && userAuthResponse.Version >= Protocol.V10a.Version) { + this.tokenManager.GetRequestToken(userAuthResponse.RequestToken).VerificationCode = userAuthResponse.VerificationCode; + return MessageProtections.None; + } + + // Hook to store the token and secret on its way down to the Consumer. + var grantRequestTokenResponse = message as UnauthorizedTokenResponse; + if (grantRequestTokenResponse != null) { + this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse); + this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).ConsumerVersion = grantRequestTokenResponse.Version; + if (grantRequestTokenResponse.RequestMessage.Callback != null) { + this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).Callback = grantRequestTokenResponse.RequestMessage.Callback; + } + + return MessageProtections.None; + } + + return null; + } + + /// <summary> + /// Performs any transformation on an incoming message that may be necessary and/or + /// validates an incoming message based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The incoming message to process.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <exception cref="ProtocolException"> + /// Thrown when the binding element rules indicate that this message is invalid and should + /// NOT be processed. + /// </exception> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + + var authorizedTokenRequest = message as AuthorizedTokenRequest; + if (authorizedTokenRequest != null) { + if (authorizedTokenRequest.Version >= Protocol.V10a.Version) { + string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode; + ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier); + return MessageProtections.None; + } + + this.VerifyThrowTokenTimeToLive(authorizedTokenRequest); + } + + var userAuthorizationRequest = message as UserAuthorizationRequest; + if (userAuthorizationRequest != null) { + this.VerifyThrowTokenTimeToLive(userAuthorizationRequest); + } + + return null; + } + + #endregion + + /// <summary> + /// Ensures that short-lived request tokens included in incoming messages have not expired. + /// </summary> + /// <param name="message">The incoming message.</param> + /// <exception cref="ProtocolException">Thrown when the token in the message has expired.</exception> + private void VerifyThrowTokenTimeToLive(ITokenContainingMessage message) { + ErrorUtilities.VerifyInternal(!(message is AccessProtectedResourceRequest), "We shouldn't be verifying TTL on access tokens."); + if (message == null) { + return; + } + + try { + IServiceProviderRequestToken token = this.tokenManager.GetRequestToken(message.Token); + TimeSpan ttl = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.MaximumRequestTokenTimeToLive; + if (DateTime.Now >= token.CreatedOn.ToLocalTime() + ttl) { + Logger.OAuth.ErrorFormat( + "OAuth token {0} rejected because it was originally issued at {1}, expired at {2}, and it is now {3}.", + token.Token, + token.CreatedOn, + token.CreatedOn + ttl, + DateTime.Now); + ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound); + } + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound); + } + } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs new file mode 100644 index 0000000..5aedc9d --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------- +// <copyright file="UriOrOobEncoding.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Reflection; + + /// <summary> + /// An URI encoder that translates null <see cref="Uri"/> references as "oob" + /// instead of an empty/missing argument. + /// </summary> + internal class UriOrOobEncoding : IMessagePartNullEncoder { + /// <summary> + /// The string constant "oob", used to indicate an out-of-band configuration. + /// </summary> + private const string OutOfBandConfiguration = "oob"; + + /// <summary> + /// Initializes a new instance of the <see cref="UriOrOobEncoding"/> class. + /// </summary> + public UriOrOobEncoding() { + } + + #region IMessagePartNullEncoder Members + + /// <summary> + /// Gets the string representation to include in a serialized message + /// when the message part has a <c>null</c> value. + /// </summary> + /// <value></value> + public string EncodedNullValue { + get { return OutOfBandConfiguration; } + } + + #endregion + + #region IMessagePartEncoder Members + + /// <summary> + /// Encodes the specified value. + /// </summary> + /// <param name="value">The value. Guaranteed to never be null.</param> + /// <returns> + /// The <paramref name="value"/> in string form, ready for message transport. + /// </returns> + public string Encode(object value) { + ErrorUtilities.VerifyArgumentNotNull(value, "value"); + + Uri uriValue = (Uri)value; + return uriValue.AbsoluteUri; + } + + /// <summary> + /// Decodes the specified value. + /// </summary> + /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param> + /// <returns> + /// The deserialized form of the given string. + /// </returns> + /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception> + public object Decode(string value) { + if (string.Equals(value, OutOfBandConfiguration, StringComparison.Ordinal)) { + return null; + } else { + return new Uri(value, UriKind.Absolute); + } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs index 3abb794..55b40ac 100644 --- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth { using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Net; + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OAuth.ChannelElements; @@ -32,6 +33,7 @@ namespace DotNetOpenAuth.OAuth { INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge); this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager); this.ServiceProvider = serviceDescription; + this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.Consumer.SecuritySettings.CreateSecuritySettings(); } /// <summary> @@ -61,6 +63,11 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Gets the security settings for this consumer. + /// </summary> + internal ConsumerSecuritySettings SecuritySettings { get; private set; } + + /// <summary> /// Gets or sets the channel to use for sending/receiving messages. /// </summary> internal OAuthChannel OAuthChannel { get; set; } @@ -167,7 +174,7 @@ namespace DotNetOpenAuth.OAuth { ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint"); ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken"); - AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) { + AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint, this.ServiceProvider.Version) { AccessToken = accessToken, ConsumerKey = this.ConsumerKey, }; @@ -189,18 +196,26 @@ namespace DotNetOpenAuth.OAuth { /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")] protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) { - // Obtain an unauthorized request token. - var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) { + // Obtain an unauthorized request token. Assume the OAuth version given in the service description. + var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version) { ConsumerKey = this.ConsumerKey, + Callback = callback, }; var tokenAccessor = this.Channel.MessageDescriptions.GetAccessor(token); tokenAccessor.AddExtraParameters(requestParameters); var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token); this.TokenManager.StoreNewRequestToken(token, requestTokenResponse); - // Request user authorization. + // Fine-tune our understanding of the SP's supported OAuth version if it's wrong. + if (this.ServiceProvider.Version != requestTokenResponse.Version) { + Logger.OAuth.WarnFormat("Expected OAuth service provider at endpoint {0} to use OAuth {1} but {2} was detected. Adjusting service description to new version.", this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version, requestTokenResponse.Version); + this.ServiceProvider.ProtocolVersion = Protocol.Lookup(requestTokenResponse.Version).ProtocolVersion; + } + + // Request user authorization. The OAuth version will automatically include + // or drop the callback that we're setting here. ITokenContainingMessage assignedRequestToken = requestTokenResponse; - var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) { + var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token, requestTokenResponse.Version) { Callback = callback, }; var requestAuthorizationAccessor = this.Channel.MessageDescriptions.GetAccessor(requestAuthorization); @@ -213,14 +228,18 @@ namespace DotNetOpenAuth.OAuth { /// Exchanges a given request token for access token. /// </summary> /// <param name="requestToken">The request token that the user has authorized.</param> - /// <returns>The access token assigned by the Service Provider.</returns> - protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) { + /// <param name="verifier">The verifier code.</param> + /// <returns> + /// The access token assigned by the Service Provider. + /// </returns> + protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) { Contract.Requires(!String.IsNullOrEmpty(requestToken)); Contract.Ensures(Contract.Result<AuthorizedTokenResponse>() != null); ErrorUtilities.VerifyNonZeroLength(requestToken, "requestToken"); - var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) { + var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) { RequestToken = requestToken, + VerificationCode = verifier, ConsumerKey = this.ConsumerKey, }; var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess); diff --git a/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs b/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs new file mode 100644 index 0000000..bb2fbaa --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// <copyright file="ConsumerSecuritySettings.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth { + /// <summary> + /// Security settings that are applicable to consumers. + /// </summary> + internal class ConsumerSecuritySettings : SecuritySettings { + /// <summary> + /// Initializes a new instance of the <see cref="ConsumerSecuritySettings"/> class. + /// </summary> + internal ConsumerSecuritySettings() { + } + } +} diff --git a/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs b/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs index ca74a77..f9c1a94 100644 --- a/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs +++ b/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs @@ -49,8 +49,25 @@ namespace DotNetOpenAuth.OAuth { /// </summary> /// <param name="requestToken">The request token that the user has authorized.</param> /// <returns>The access token assigned by the Service Provider.</returns> - public new AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) { - return base.ProcessUserAuthorization(requestToken); + [Obsolete("Use the ProcessUserAuthorization method that takes a verifier parameter instead.")] + public AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) { + return this.ProcessUserAuthorization(requestToken, null); + } + + /// <summary> + /// Exchanges a given request token for access token. + /// </summary> + /// <param name="requestToken">The request token that the user has authorized.</param> + /// <param name="verifier">The verifier code typed in by the user. Must not be <c>Null</c> for OAuth 1.0a service providers and later.</param> + /// <returns> + /// The access token assigned by the Service Provider. + /// </returns> + public new AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) { + if (this.ServiceProvider.Version >= Protocol.V10a.Version) { + ErrorUtilities.VerifyNonZeroLength(verifier, "verifier"); + } + + return base.ProcessUserAuthorization(requestToken, verifier); } } } diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index 62e02de..b60fda4 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -17,8 +18,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs index 2d4793c..1228290 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Globalization; using DotNetOpenAuth.Messaging; @@ -20,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AuthorizedTokenRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> @@ -33,6 +35,13 @@ namespace DotNetOpenAuth.OAuth.Messages { } /// <summary> + /// Gets or sets the verification code received by the Consumer from the Service Provider + /// in the <see cref="UserAuthorizationResponse.VerificationCode"/> property. + /// </summary> + [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)] + public string VerificationCode { get; set; } + + /// <summary> /// Gets or sets the unauthorized Request Token used to obtain authorization. /// </summary> [MessagePart("oauth_token", IsRequired = true)] diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs index 14413a5..0b14819 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -19,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="originatingRequest">The originating request.</param> protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest) - : base(MessageProtections.None, originatingRequest) { + : base(MessageProtections.None, originatingRequest, originatingRequest.Version) { } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs index e0269db..89e0276 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs @@ -62,12 +62,15 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="protectionRequired">The level of protection the message requires.</param> /// <param name="originatingRequest">The request that asked for this direct response.</param> - protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) { + /// <param name="version">The OAuth version.</param> + protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest, Version version) { ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest"); + ErrorUtilities.VerifyArgumentNotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = MessageTransport.Direct; this.originatingRequest = originatingRequest; + this.Version = version; } /// <summary> @@ -76,14 +79,15 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <param name="protectionRequired">The level of protection the message requires.</param> /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param> /// <param name="recipient">The URI that a directed message will be delivered to.</param> - protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) { - if (recipient == null) { - throw new ArgumentNullException("recipient"); - } + /// <param name="version">The OAuth version.</param> + protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient, Version version) { + ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient"); + ErrorUtilities.VerifyArgumentNotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = transport; this.recipient = recipient; + this.Version = version; } #region IProtocolMessage Properties @@ -163,9 +167,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <summary> /// Gets the version of the protocol this message is prepared to implement. /// </summary> - protected virtual Version Version { - get { return new Version(1, 0); } - } + protected internal Version Version { get; private set; } /// <summary> /// Gets the level of protection this message requires. diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs index d1abb58..1d8ca21 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs @@ -31,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param> /// <param name="recipient">The URI that a directed message will be delivered to.</param> - internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient) - : base(MessageProtections.All, transport, recipient) { + /// <param name="version">The OAuth version.</param> + internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient, Version version) + : base(MessageProtections.All, transport, recipient, version) { ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this; HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods; self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET"; @@ -164,7 +165,7 @@ namespace DotNetOpenAuth.OAuth.Messages { [MessagePart("oauth_version", IsRequired = false)] private string OAuthVersion { get { - return Version.ToString(); + return Protocol.Lookup(Version).PublishedVersion; } set { diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs index e491bad..9214d91 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs @@ -5,8 +5,10 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Collections.Generic; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.ChannelElements; /// <summary> /// A direct message sent from Consumer to Service Provider to request a Request Token. @@ -16,11 +18,23 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UnauthorizedTokenRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> + /// Gets or sets the absolute URL to which the Service Provider will redirect the + /// User back when the Obtaining User Authorization step is completed. + /// </summary> + /// <value> + /// The callback URL; or <c>null</c> if the Consumer is unable to receive + /// callbacks or a callback URL has been established via other means. + /// </value> + [MessagePart("oauth_callback", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion, Encoder = typeof(UriOrOobEncoding))] + public Uri Callback { get; set; } + + /// <summary> /// Gets the extra, non-OAuth parameters that will be included in the message. /// </summary> public new IDictionary<string, string> ExtraData { diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs index 285dec7..8ccd8e3 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs @@ -25,7 +25,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// This constructor is used by the Service Provider to send the message. /// </remarks> protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret) - : this(requestMessage) { + : this(requestMessage, requestMessage.Version) { ErrorUtilities.VerifyArgumentNotNull(requestToken, "requestToken"); ErrorUtilities.VerifyArgumentNotNull(tokenSecret, "tokenSecret"); @@ -37,9 +37,10 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UnauthorizedTokenResponse"/> class. /// </summary> /// <param name="originatingRequest">The originating request.</param> + /// <param name="version">The OAuth version.</param> /// <remarks>This constructor is used by the consumer to deserialize the message.</remarks> - protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest) - : base(MessageProtections.None, originatingRequest) { + protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest, Version version) + : base(MessageProtections.None, originatingRequest, version) { } /// <summary> @@ -84,5 +85,13 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> [MessagePart("oauth_token_secret", IsRequired = true)] protected internal string TokenSecret { get; set; } + + /// <summary> + /// Gets a value indicating whether the Service Provider recognized the callback parameter in the request. + /// </summary> + [MessagePart("oauth_callback_confirmed", IsRequired = true, MinVersion = Protocol.V10aVersion)] + private bool CallbackConfirmed { + get { return true; } + } } } diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs index f1af0bc..099729e 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs @@ -21,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> /// <param name="requestToken">The request token.</param> - internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken) - : this(serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken, Version version) + : this(serviceProvider, version) { this.RequestToken = requestToken; } @@ -30,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UserAuthorizationRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider, version) { } /// <summary> @@ -51,6 +53,14 @@ namespace DotNetOpenAuth.OAuth.Messages { } /// <summary> + /// Gets a value indicating whether this is a safe OAuth authorization request. + /// </summary> + /// <value><c>true</c> if the Consumer is using OAuth 1.0a or later; otherwise, <c>false</c>.</value> + public bool IsUnsafeRequest { + get { return this.Version < Protocol.V10a.Version; } + } + + /// <summary> /// Gets or sets the Request Token obtained in the previous step. /// </summary> /// <remarks> @@ -65,7 +75,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Gets or sets a URL the Service Provider will use to redirect the User back /// to the Consumer when Obtaining User Authorization is complete. Optional. /// </summary> - [MessagePart("oauth_callback", IsRequired = false)] + [MessagePart("oauth_callback", IsRequired = false, MaxVersion = "1.0")] internal Uri Callback { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs index da6a909..69a327c 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs @@ -19,8 +19,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UserAuthorizationResponse"/> class. /// </summary> /// <param name="consumer">The URI of the Consumer endpoint to send this message to.</param> - internal UserAuthorizationResponse(Uri consumer) - : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest)) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationResponse(Uri consumer, Version version) + : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest), version) { } /// <summary> @@ -32,6 +33,20 @@ namespace DotNetOpenAuth.OAuth.Messages { } /// <summary> + /// Gets or sets the verification code that must accompany the request to exchange the + /// authorized request token for an access token. + /// </summary> + /// <value>An unguessable value passed to the Consumer via the User and REQUIRED to complete the process.</value> + /// <remarks> + /// If the Consumer did not provide a callback URL, the Service Provider SHOULD display the value of the + /// verification code, and instruct the User to manually inform the Consumer that authorization is + /// completed. If the Service Provider knows a Consumer to be running on a mobile device or set-top box, + /// the Service Provider SHOULD ensure that the verifier value is suitable for manual entry. + /// </remarks> + [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)] + public string VerificationCode { get; set; } + + /// <summary> /// Gets or sets the Request Token. /// </summary> [MessagePart("oauth_token", IsRequired = true)] diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index 6eec124..3593446 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -79,20 +79,20 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Looks up a localized string similar to The RSA-SHA1 signing binding element's consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified.. + /// Looks up a localized string similar to Failure looking up secret for consumer or token.. /// </summary> - internal static string ConsumerCertificateProviderNotAvailable { + internal static string ConsumerOrTokenSecretNotFound { get { - return ResourceManager.GetString("ConsumerCertificateProviderNotAvailable", resourceCulture); + return ResourceManager.GetString("ConsumerOrTokenSecretNotFound", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to Failure looking up secret for consumer or token.. + /// Looks up a localized string similar to oauth_verifier argument was incorrect.. /// </summary> - internal static string ConsumerOrTokenSecretNotFound { + internal static string IncorrectVerifier { get { - return ResourceManager.GetString("ConsumerOrTokenSecretNotFound", resourceCulture); + return ResourceManager.GetString("IncorrectVerifier", resourceCulture); } } @@ -133,6 +133,15 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.. + /// </summary> + internal static string MinimumConsumerVersionRequirementNotMet { + get { + return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The request URL query MUST NOT contain any OAuth Protocol Parameters.. /// </summary> internal static string RequestUrlMustNotHaveOAuthParameters { @@ -160,6 +169,15 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Looks up a localized string similar to A token in the message was not recognized by the service provider.. + /// </summary> + internal static string TokenNotFound { + get { + return ResourceManager.GetString("TokenNotFound", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The RSA-SHA1 signing binding element has not been set with a certificate for signing.. /// </summary> internal static string X509CertificateNotProvidedForSigning { diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index 0aa48f9..bbeeda9 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -123,18 +123,21 @@ <data name="BadAccessTokenInProtectedResourceRequest" xml:space="preserve"> <value>The access token '{0}' is invalid or expired.</value> </data> - <data name="ConsumerCertificateProviderNotAvailable" xml:space="preserve"> - <value>The RSA-SHA1 signing binding element's consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified.</value> - </data> <data name="ConsumerOrTokenSecretNotFound" xml:space="preserve"> <value>Failure looking up secret for consumer or token.</value> </data> + <data name="IncorrectVerifier" xml:space="preserve"> + <value>oauth_verifier argument was incorrect.</value> + </data> <data name="InvalidIncomingMessage" xml:space="preserve"> <value>An invalid OAuth message received and discarded.</value> </data> <data name="MessageNotAllowedExtraParameters" xml:space="preserve"> <value>The {0} message included extra data which is not allowed.</value> </data> + <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve"> + <value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value> + </data> <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve"> <value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value> </data> @@ -150,6 +153,9 @@ <data name="SigningElementsMustShareSameProtection" xml:space="preserve"> <value>All signing elements must offer the same message protection.</value> </data> + <data name="TokenNotFound" xml:space="preserve"> + <value>A token in the message was not recognized by the service provider.</value> + </data> <data name="X509CertificateNotProvidedForSigning" xml:space="preserve"> <value>The RSA-SHA1 signing binding element has not been set with a certificate for signing.</value> </data> diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs index 88615ff..f535b10 100644 --- a/src/DotNetOpenAuth/OAuth/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth/Protocol.cs @@ -7,17 +7,34 @@ namespace DotNetOpenAuth.OAuth { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Text; using DotNetOpenAuth.Messaging; /// <summary> + /// An enumeration of the OAuth protocol versions supported by this library. + /// </summary> + public enum ProtocolVersion { + /// <summary> + /// OAuth 1.0 specification + /// </summary> + V10, + + /// <summary> + /// OAuth 1.0a specification + /// </summary> + V10a, + } + + /// <summary> /// Constants used in the OAuth protocol. /// </summary> /// <remarks> /// OAuth Protocol Parameter names and values are case sensitive. Each OAuth Protocol Parameters MUST NOT appear more than once per request, and are REQUIRED unless otherwise noted, /// per OAuth 1.0 section 5. /// </remarks> + [DebuggerDisplay("OAuth {Version}")] internal class Protocol { /// <summary> /// The namespace to use for V1.0 of the protocol. @@ -25,63 +42,105 @@ namespace DotNetOpenAuth.OAuth { internal const string DataContractNamespaceV10 = "http://oauth.net/core/1.0/"; /// <summary> + /// The prefix used for all key names in the protocol. + /// </summary> + internal const string ParameterPrefix = "oauth_"; + + /// <summary> + /// The string representation of a <see cref="Version"/> instance to be used to represent OAuth 1.0a. + /// </summary> + internal const string V10aVersion = "1.0.1"; + + /// <summary> + /// The scheme to use in Authorization header message requests. + /// </summary> + internal const string AuthorizationHeaderScheme = "OAuth"; + + /// <summary> /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol. /// </summary> internal static readonly Protocol V10 = new Protocol { dataContractNamespace = DataContractNamespaceV10, + Version = new Version(1, 0), + ProtocolVersion = ProtocolVersion.V10, }; /// <summary> + /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0a of the protocol. + /// </summary> + internal static readonly Protocol V10a = new Protocol { + dataContractNamespace = DataContractNamespaceV10, + Version = new Version(V10aVersion), + ProtocolVersion = ProtocolVersion.V10a, + }; + + /// <summary> + /// A list of all supported OAuth versions, in order starting from newest version. + /// </summary> + internal static readonly List<Protocol> AllVersions = new List<Protocol>() { V10a, V10 }; + + /// <summary> + /// The default (or most recent) supported version of the OpenID protocol. + /// </summary> + internal static readonly Protocol Default = AllVersions[0]; + + /// <summary> /// The namespace to use for this version of the protocol. /// </summary> private string dataContractNamespace; /// <summary> - /// The prefix used for all key names in the protocol. + /// Initializes a new instance of the <see cref="Protocol"/> class. /// </summary> - private string parameterPrefix = "oauth_"; + internal Protocol() { + this.PublishedVersion = "1.0"; + } /// <summary> - /// The scheme to use in Authorization header message requests. + /// Gets the version used to represent OAuth 1.0a. /// </summary> - private string authorizationHeaderScheme = "OAuth"; + internal Version Version { get; private set; } /// <summary> - /// Gets the default <see cref="Protocol"/> instance. + /// Gets the version to declare on the wire. /// </summary> - internal static Protocol Default { get { return V10; } } + internal string PublishedVersion { get; private set; } /// <summary> - /// Gets the namespace to use for this version of the protocol. + /// Gets the <see cref="ProtocolVersion"/> enum value for the <see cref="Protocol"/> instance. /// </summary> - internal string DataContractNamespace { - get { return this.dataContractNamespace; } - } + internal ProtocolVersion ProtocolVersion { get; private set; } /// <summary> - /// Gets the prefix used for all key names in the protocol. + /// Gets the namespace to use for this version of the protocol. /// </summary> - internal string ParameterPrefix { - get { return this.parameterPrefix; } + internal string DataContractNamespace { + get { return this.dataContractNamespace; } } /// <summary> - /// Gets the scheme to use in Authorization header message requests. + /// Gets the OAuth Protocol instance to use for the given version. /// </summary> - internal string AuthorizationHeaderScheme { - get { return this.authorizationHeaderScheme; } + /// <param name="version">The OAuth version to get.</param> + /// <returns>A matching <see cref="Protocol"/> instance.</returns> + public static Protocol Lookup(ProtocolVersion version) { + switch (version) { + case ProtocolVersion.V10: return Protocol.V10; + case ProtocolVersion.V10a: return Protocol.V10a; + default: throw new ArgumentOutOfRangeException("version"); + } } /// <summary> - /// Gets an instance of <see cref="Protocol"/> given a <see cref="Version"/>. + /// Gets the OAuth Protocol instance to use for the given version. /// </summary> - /// <param name="version">The version of the protocol that is desired.</param> - /// <returns>The <see cref="Protocol"/> instance representing the requested version.</returns> + /// <param name="version">The OAuth version to get.</param> + /// <returns>A matching <see cref="Protocol"/> instance.</returns> internal static Protocol Lookup(Version version) { - switch (version.Major) { - case 1: return Protocol.V10; - default: throw new ArgumentOutOfRangeException("version"); - } + ErrorUtilities.VerifyArgumentNotNull(version, "version"); + Protocol protocol = AllVersions.FirstOrDefault(p => p.Version == version); + ErrorUtilities.VerifyArgumentInRange(protocol != null, "version"); + return protocol; } } } diff --git a/src/DotNetOpenAuth/OAuth/SecuritySettings.cs b/src/DotNetOpenAuth/OAuth/SecuritySettings.cs new file mode 100644 index 0000000..3329f09 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/SecuritySettings.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// <copyright file="SecuritySettings.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth { + /// <summary> + /// Security settings that may be applicable to both consumers and service providers. + /// </summary> + public class SecuritySettings { + /// <summary> + /// Initializes a new instance of the <see cref="SecuritySettings"/> class. + /// </summary> + protected SecuritySettings() { + } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs index 122e7ee..8141126 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs @@ -6,11 +6,13 @@ namespace DotNetOpenAuth.OAuth { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.ServiceModel.Channels; using System.Web; + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OAuth.ChannelElements; @@ -33,6 +35,11 @@ namespace DotNetOpenAuth.OAuth { /// </remarks> public class ServiceProvider : IDisposable { /// <summary> + /// The length of the verifier code (in raw bytes before base64 encoding) to generate. + /// </summary> + private const int VerifierCodeLength = 5; + + /// <summary> /// The field behind the <see cref="OAuthChannel"/> property. /// </summary> private OAuthChannel channel; @@ -52,7 +59,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, ITokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) { + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) { ErrorUtilities.VerifyArgumentNotNull(serviceDescription, "serviceDescription"); ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); ErrorUtilities.VerifyArgumentNotNull(messageTypeProvider, "messageTypeProvider"); @@ -62,6 +69,7 @@ namespace DotNetOpenAuth.OAuth { this.ServiceDescription = serviceDescription; this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, messageTypeProvider); this.TokenGenerator = new StandardTokenGenerator(); + this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.CreateSecuritySettings(); } /// <summary> @@ -77,8 +85,8 @@ namespace DotNetOpenAuth.OAuth { /// <summary> /// Gets the persistence store for tokens and secrets. /// </summary> - public ITokenManager TokenManager { - get { return this.OAuthChannel.TokenManager; } + public IServiceProviderTokenManager TokenManager { + get { return (IServiceProviderTokenManager)this.OAuthChannel.TokenManager; } } /// <summary> @@ -89,6 +97,11 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Gets the security settings for this service provider. + /// </summary> + public ServiceProviderSecuritySettings SecuritySettings { get; private set; } + + /// <summary> /// Gets or sets the channel to use for sending/receiving messages. /// </summary> internal OAuthChannel OAuthChannel { @@ -97,15 +110,38 @@ namespace DotNetOpenAuth.OAuth { } set { - if (this.channel != null) { - this.channel.Sending -= this.OAuthChannel_Sending; - } - + Contract.Requires(value != null); + ErrorUtilities.VerifyArgumentNotNull(value, "value"); this.channel = value; + } + } - if (this.channel != null) { - this.channel.Sending += this.OAuthChannel_Sending; - } + /// <summary> + /// Creates a cryptographically strong random verification code. + /// </summary> + /// <param name="format">The desired format of the verification code.</param> + /// <param name="length">The length of the code. + /// When <paramref name="format"/> is <see cref="VerificationCodeFormat.IncludedInCallback"/>, + /// this is the length of the original byte array before base64 encoding rather than the actual + /// length of the final string.</param> + /// <returns>The verification code.</returns> + public static string CreateVerificationCode(VerificationCodeFormat format, int length) { + Contract.Requires(length >= 0); + ErrorUtilities.VerifyArgumentInRange(length >= 0, "length"); + + switch (format) { + case VerificationCodeFormat.IncludedInCallback: + return MessagingUtilities.GetCryptoRandomDataAsBase64(length); + case VerificationCodeFormat.AlphaNumericNoLookAlikes: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.AlphaNumericNoLookAlikes); + case VerificationCodeFormat.AlphaUpper: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.UppercaseLetters); + case VerificationCodeFormat.AlphaLower: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.LowercaseLetters); + case VerificationCodeFormat.Numeric: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.Digits); + default: + throw new ArgumentOutOfRangeException("format"); } } @@ -149,7 +185,9 @@ namespace DotNetOpenAuth.OAuth { /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> public UnauthorizedTokenRequest ReadTokenRequest(HttpRequestInfo request) { UnauthorizedTokenRequest message; - this.Channel.TryReadFromRequest(request, out message); + if (this.Channel.TryReadFromRequest(request, out message)) { + ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version); + } return message; } @@ -160,9 +198,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="request">The token request message the Consumer sent that the Service Provider is now responding to.</param> /// <returns>The response message to send using the <see cref="Channel"/>, after optionally adding extra data to it.</returns> public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) { - if (request == null) { - throw new ArgumentNullException("request"); - } + ErrorUtilities.VerifyArgumentNotNull(request, "request"); string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey); string secret = this.TokenGenerator.GenerateSecret(); @@ -278,11 +314,27 @@ namespace DotNetOpenAuth.OAuth { Contract.Requires(request != null); ErrorUtilities.VerifyArgumentNotNull(request, "request"); - if (request.Callback != null) { - return this.PrepareAuthorizationResponse(request, request.Callback); + // It is very important for us to ignore the oauth_callback argument in the + // UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we + // open up a security exploit. + IServiceProviderRequestToken token = this.TokenManager.GetRequestToken(request.RequestToken); + Uri callback; + if (request.Version >= Protocol.V10a.Version) { + // In OAuth 1.0a, we'll prefer the token-specific callback to the pre-registered one. + if (token.Callback != null) { + callback = token.Callback; + } else { + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback; + } } else { - return null; + // In OAuth 1.0, we'll prefer the pre-registered callback over the token-specific one + // since 1.0 has a security weakness for user-modified callback URIs. + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback ?? request.Callback; } + + return callback != null ? this.PrepareAuthorizationResponse(request, callback) : null; } /// <summary> @@ -291,7 +343,7 @@ namespace DotNetOpenAuth.OAuth { /// </summary> /// <param name="request">The Consumer's original authorization request.</param> /// <param name="callback">The callback URI the consumer has previously registered - /// with this service provider.</param> + /// with this service provider or that came in the <see cref="UnauthorizedTokenRequest"/>.</param> /// <returns> /// The message to send to the Consumer using <see cref="Channel"/>. /// </returns> @@ -302,9 +354,14 @@ namespace DotNetOpenAuth.OAuth { ErrorUtilities.VerifyArgumentNotNull(request, "request"); ErrorUtilities.VerifyArgumentNotNull(callback, "callback"); - var authorization = new UserAuthorizationResponse(request.Callback) { + var authorization = new UserAuthorizationResponse(callback, request.Version) { RequestToken = request.RequestToken, }; + + if (authorization.Version >= Protocol.V10a.Version) { + authorization.VerificationCode = CreateVerificationCode(VerificationCodeFormat.IncludedInCallback, VerifierCodeLength); + } + return authorization; } @@ -338,17 +395,10 @@ namespace DotNetOpenAuth.OAuth { /// <param name="request">The Consumer's message requesting an access token.</param> /// <returns>The HTTP response to actually send to the Consumer.</returns> public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) { - if (request == null) { - throw new ArgumentNullException("request"); - } + Contract.Requires(request != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); - if (!this.TokenManager.IsRequestTokenAuthorized(request.RequestToken)) { - throw new ProtocolException( - string.Format( - CultureInfo.CurrentCulture, - OAuthStrings.AccessTokenNotAuthorized, - request.RequestToken)); - } + ErrorUtilities.VerifyProtocol(this.TokenManager.IsRequestTokenAuthorized(request.RequestToken), OAuthStrings.AccessTokenNotAuthorized, request.RequestToken); string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey); string tokenSecret = this.TokenGenerator.GenerateSecret(); @@ -440,18 +490,5 @@ namespace DotNetOpenAuth.OAuth { } #endregion - - /// <summary> - /// Hooks the channel in order to perform some operations on some outgoing messages. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="DotNetOpenAuth.Messaging.ChannelEventArgs"/> instance containing the event data.</param> - private void OAuthChannel_Sending(object sender, ChannelEventArgs e) { - // Hook to store the token and secret on its way down to the Consumer. - var grantRequestTokenResponse = e.Message as UnauthorizedTokenResponse; - if (grantRequestTokenResponse != null) { - this.TokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse); - } - } } } diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs index 4636829..9014762 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs @@ -26,9 +26,15 @@ namespace DotNetOpenAuth.OAuth { /// Initializes a new instance of the <see cref="ServiceProviderDescription"/> class. /// </summary> public ServiceProviderDescription() { + this.ProtocolVersion = Protocol.Default.ProtocolVersion; } /// <summary> + /// Gets or sets the OAuth version supported by the Service Provider. + /// </summary> + public ProtocolVersion ProtocolVersion { get; set; } + + /// <summary> /// Gets or sets the URL used to obtain an unauthorized Request Token, /// described in Section 6.1 (Obtaining an Unauthorized Request Token). /// </summary> @@ -43,7 +49,7 @@ namespace DotNetOpenAuth.OAuth { } set { - if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.V10.ParameterPrefix)) { + if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.ParameterPrefix)) { throw new ArgumentException(OAuthStrings.RequestUrlMustNotHaveOAuthParameters); } @@ -77,6 +83,13 @@ namespace DotNetOpenAuth.OAuth { public ITamperProtectionChannelBindingElement[] TamperProtectionElements { get; set; } /// <summary> + /// Gets the OAuth version supported by the Service Provider. + /// </summary> + internal Version Version { + get { return Protocol.Lookup(this.ProtocolVersion).Version; } + } + + /// <summary> /// Creates a signing element that includes all the signing elements this service provider supports. /// </summary> /// <returns>The created signing element.</returns> diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs new file mode 100644 index 0000000..b8e12fd --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------- +// <copyright file="ServiceProviderSecuritySettings.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth { + using System; + + /// <summary> + /// Security settings that are applicable to service providers. + /// </summary> + public class ServiceProviderSecuritySettings : SecuritySettings { + /// <summary> + /// Initializes a new instance of the <see cref="ServiceProviderSecuritySettings"/> class. + /// </summary> + internal ServiceProviderSecuritySettings() { + } + + /// <summary> + /// Gets or sets the minimum required version of OAuth that must be implemented by a Consumer. + /// </summary> + public ProtocolVersion MinimumRequiredOAuthVersion { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs new file mode 100644 index 0000000..d25c988 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------- +// <copyright file="VerificationCodeFormat.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth { + /// <summary> + /// The different formats a user authorization verifier code can take + /// in order to be as secure as possible while being compatible with + /// the type of OAuth Consumer requesting access. + /// </summary> + /// <remarks> + /// Some Consumers may be set-top boxes, video games, mobile devies, etc. + /// with very limited character entry support and no ability to receive + /// a callback URI. OAuth 1.0a requires that these devices operators + /// must manually key in a verifier code, so in these cases it better + /// be possible to do so given the input options on that device. + /// </remarks> + public enum VerificationCodeFormat { + /// <summary> + /// The strongest verification code. + /// The best option for web consumers since a callback is usually an option. + /// </summary> + IncludedInCallback, + + /// <summary> + /// A combination of upper and lowercase letters and numbers may be used, + /// allowing a computer operator to easily read from the screen and key + /// in the verification code. + /// </summary> + /// <remarks> + /// Some letters and numbers will be skipped where they are visually similar + /// enough that they can be difficult to distinguish when displayed with most fonts. + /// </remarks> + AlphaNumericNoLookAlikes, + + /// <summary> + /// Only uppercase letters will be used in the verification code. + /// Verification codes are case-sensitive, so consumers with fixed + /// keyboards with only one character case option may require this option. + /// </summary> + AlphaUpper, + + /// <summary> + /// Only lowercase letters will be used in the verification code. + /// Verification codes are case-sensitive, so consumers with fixed + /// keyboards with only one character case option may require this option. + /// </summary> + AlphaLower, + + /// <summary> + /// Only the numbers 0-9 will be used in the verification code. + /// Must useful for consumers running on mobile phone devices. + /// </summary> + Numeric, + } +} diff --git a/src/DotNetOpenAuth/OAuth/WebConsumer.cs b/src/DotNetOpenAuth/OAuth/WebConsumer.cs index 689a795..56d3029 100644 --- a/src/DotNetOpenAuth/OAuth/WebConsumer.cs +++ b/src/DotNetOpenAuth/OAuth/WebConsumer.cs @@ -42,7 +42,7 @@ namespace DotNetOpenAuth.OAuth { /// Requires HttpContext.Current. /// </remarks> public UserAuthorizationRequest PrepareRequestUserAuthorization() { - Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.Default.ParameterPrefix); + Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.ParameterPrefix); return this.PrepareRequestUserAuthorization(callback, null, null); } @@ -120,7 +120,7 @@ namespace DotNetOpenAuth.OAuth { } // Prepare a message to exchange the request token for an access token. - var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) { + var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) { RequestToken = positiveAuthorization.RequestToken, ConsumerKey = this.ConsumerKey, }; @@ -147,7 +147,8 @@ namespace DotNetOpenAuth.OAuth { UserAuthorizationResponse authorizationMessage; if (this.Channel.TryReadFromRequest<UserAuthorizationResponse>(request, out authorizationMessage)) { string requestToken = authorizationMessage.RequestToken; - return this.ProcessUserAuthorization(requestToken); + string verifier = authorizationMessage.VerificationCode; + return this.ProcessUserAuthorization(requestToken, verifier); } else { return null; } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs index 9729333..a3f64ab 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs @@ -88,8 +88,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { bool countSent = false; string countString; if (fields.TryGetValue("count." + alias, out countString)) { - if (!int.TryParse(countString, out count) || count <= 0) { - Logger.OpenId.ErrorFormat("Failed to parse count.{0} value to a positive integer.", alias); + if (!int.TryParse(countString, out count) || count < 0) { + Logger.OpenId.ErrorFormat("Failed to parse count.{0} value to a non-negative integer.", alias); continue; } countSent = true; diff --git a/src/DotNetOpenAuth/OpenId/Protocol.cs b/src/DotNetOpenAuth/OpenId/Protocol.cs index b9f2cca..7b8a2f1 100644 --- a/src/DotNetOpenAuth/OpenId/Protocol.cs +++ b/src/DotNetOpenAuth/OpenId/Protocol.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId { using DotNetOpenAuth.Messaging; using System.Globalization; using System.Diagnostics.CodeAnalysis; + using System.Diagnostics; /// <summary> /// An enumeration of the OpenID protocol versions supported by this library. @@ -34,6 +35,7 @@ namespace DotNetOpenAuth.OpenId { /// Tracks the several versions of OpenID this library supports and the unique /// constants to each version used in the protocol. /// </summary> + [DebuggerDisplay("OpenID {Version}")] internal class Protocol { /// <summary> /// The value of the openid.ns parameter in the OpenID 2.0 specification. diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs index bec510b..43e666d 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs @@ -100,8 +100,9 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Sends the response for the <see cref="PendingAuthenticationRequest"/> and clears the property. /// </summary> public static void SendResponse() { - Provider.SendResponse(PendingAuthenticationRequest); + var pendingRequest = PendingAuthenticationRequest; PendingAuthenticationRequest = null; + Provider.SendResponse(pendingRequest); } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs index a37c8c7..85c0096 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Linq; + using System.Net; using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; @@ -209,6 +210,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany); } } catch (ProtocolException ex) { + // If the association failed because the remote server can't handle Expect: 100 Continue headers, + // then our web request handler should have already accomodated for future calls. Go ahead and + // immediately make one of those future calls now to try to get the association to succeed. + if (StandardWebRequestHandler.IsExceptionFrom417ExpectationFailed(ex)) { + return this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1); + } + // Since having associations with OPs is not totally critical, we'll log and eat // the exception so that auth may continue in dumb mode. Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Endpoint, ex); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs index 2daf205..f661789 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs @@ -906,12 +906,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { StringBuilder discoveryResultBuilder = new StringBuilder(); discoveryResultBuilder.Append("{"); try { - List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true); + List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true).Where(req => this.OnLoggingIn(req)).ToList(); if (requests.Count > 0) { discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests[0].ClaimedIdentifier)); discoveryResultBuilder.Append("requests: ["); foreach (IAuthenticationRequest request in requests) { - this.OnLoggingIn(request); discoveryResultBuilder.Append("{"); discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri)); request.Mode = AuthenticationRequestMode.Immediate; @@ -994,6 +993,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { writer.WriteBeginTag("span"); writer.WriteAttribute("class", this.CssClass); writer.Write(" style='"); + writer.WriteStyleAttribute("display", "inline-block"); writer.WriteStyleAttribute("position", "relative"); writer.WriteStyleAttribute("font-size", "16px"); writer.Write("'>"); @@ -1087,11 +1087,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Fires the <see cref="LoggingIn"/> event. /// </summary> /// <param name="request">The request.</param> - private void OnLoggingIn(IAuthenticationRequest request) { + /// <returns><c>true</c> if the login should proceed; <c>false</c> otherwise.</returns> + private bool OnLoggingIn(IAuthenticationRequest request) { var loggingIn = this.LoggingIn; if (loggingIn != null) { - loggingIn(this, new OpenIdEventArgs(request)); + var args = new OpenIdEventArgs(request); + loggingIn(this, args); + return !args.Cancel; } + + return true; } /// <summary> @@ -1231,7 +1236,7 @@ if (!openidbox.dnoi_internal.onSubmit()) {{ return false; }} /// requests should be initialized for use in invisible iframes for background authentication.</param> /// <returns>The list of authentication requests, any one of which may be /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns> - private List<IAuthenticationRequest> CreateRequests(string userSuppliedIdentifier, bool immediate) { + private IEnumerable<IAuthenticationRequest> CreateRequests(string userSuppliedIdentifier, bool immediate) { var requests = new List<IAuthenticationRequest>(); // Approximate the returnTo (either based on the customize property or the page URL) diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js index fc6a68c..6e5d7dd 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js @@ -126,9 +126,10 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url box.dnoi_internal.authenticationIFrames = new FrameManager(throttle); box.dnoi_internal.constructButton = function(text, tooltip, onclick) { - var button = document.createElement('button'); + var button = document.createElement('input'); button.textContent = text; // Mozilla button.value = text; // IE + button.type = 'button'; button.title = tooltip != null ? tooltip : ''; button.onclick = onclick; button.style.visibility = 'hidden'; @@ -215,6 +216,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url }); box.dnoi_internal.openid_logo = box.dnoi_internal.constructIcon(openid_logo_url, null, false, true); box.dnoi_internal.op_logo = box.dnoi_internal.constructIcon('', authenticatedByToolTip, false, false, "16px"); + box.dnoi_internal.op_logo.style.maxWidth = '16px'; box.dnoi_internal.spinner = box.dnoi_internal.constructIcon(spinner_url, busyToolTip, true); box.dnoi_internal.success_icon = box.dnoi_internal.constructIcon(success_icon_url, authenticatedAsToolTip, true); //box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true); @@ -247,7 +249,11 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url box.dnoi_internal.op_logo.src = opLogo; box.dnoi_internal.op_logo.style.visibility = 'visible'; box.dnoi_internal.op_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost()); - } else { + } + trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize); + if (opLogo == null || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) { + trace('recovering from missing OP icon'); + box.dnoi_internal.op_logo.style.visibility = 'hidden'; box.dnoi_internal.openid_logo.style.visibility = 'visible'; box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost()); } @@ -285,8 +291,9 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url } box.dnoi_internal.isBusy = function() { - return box.dnoi_internal.state == 'discovering' || - box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier].busy(); + var lastDiscovery = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier]; + return box.dnoi_internal.state == 'discovering' || + (lastDiscovery && lastDiscovery.busy()); }; box.dnoi_internal.canAttemptLogin = function() { @@ -516,7 +523,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url trace('iframe hosting ' + self.endpoint + ' now OPENING.'); self.iframe = iframe; //trace('initiating auth attempt with: ' + self.immediate); - return self.immediate; + return self.immediate.toString(); }; this.trySetup = function() { self.abort(); // ensure no concurrent attempts @@ -719,8 +726,10 @@ function Uri(url) { var queryStringPairs = this.queryString.split('&'); for (var i = 0; i < queryStringPairs.length; i++) { - var pair = queryStringPairs[i].split('='); - this.Pairs.push(new KeyValuePair(unescape(pair[0]), unescape(pair[1]))) + var equalsAt = queryStringPairs[i].indexOf('='); + left = (equalsAt >= 0) ? queryStringPairs[i].substring(0, equalsAt) : null; + right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i]; + this.Pairs.push(new KeyValuePair(unescape(left), unescape(right))); } }; diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs index 615dc9c..3cd95b0 100644 --- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs @@ -324,7 +324,7 @@ namespace DotNetOpenAuth.OpenId { foreach (var protocol in Protocol.AllPracticalVersions) { // rel attributes are supposed to be interpreted with case INsensitivity, // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes) - var serverLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)); + var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)); if (serverLinkTag == null) { continue; } @@ -333,7 +333,7 @@ namespace DotNetOpenAuth.OpenId { if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) { // See if a LocalId tag of the discovered version exists Identifier providerLocalIdentifier = null; - var delegateLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)); + var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)); if (delegateLinkTag != null) { if (Identifier.IsValid(delegateLinkTag.Href)) { providerLocalIdentifier = delegateLinkTag.Href; diff --git a/src/DotNetOpenAuth/Yadis/HtmlParser.cs b/src/DotNetOpenAuth/Yadis/HtmlParser.cs index 5a00da8..406cb4b 100644 --- a/src/DotNetOpenAuth/Yadis/HtmlParser.cs +++ b/src/DotNetOpenAuth/Yadis/HtmlParser.cs @@ -5,8 +5,11 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Yadis { + using System; using System.Collections.Generic; + using System.Diagnostics.Contracts; using System.Globalization; + using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Web; @@ -88,6 +91,19 @@ namespace DotNetOpenAuth.Yadis { } /// <summary> + /// Filters a list of controls based on presence of an attribute. + /// </summary> + /// <typeparam name="T">The type of HTML controls being filtered.</typeparam> + /// <param name="sequence">The sequence.</param> + /// <param name="attribute">The attribute.</param> + /// <returns>A filtered sequence of attributes.</returns> + internal static IEnumerable<T> WithAttribute<T>(this IEnumerable<T> sequence, string attribute) where T : HtmlControl { + Contract.Requires(sequence != null); + Contract.Requires(!String.IsNullOrEmpty(attribute)); + return sequence.Where(tag => tag.Attributes[attribute] != null); + } + + /// <summary> /// Generates a regular expression that will find a given HTML tag. /// </summary> /// <param name="tagName">Name of the tag.</param> diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs index 0caffb6..14aea62 100644 --- a/src/DotNetOpenAuth/Yadis/Yadis.cs +++ b/src/DotNetOpenAuth/Yadis/Yadis.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.Yadis { using System.Net.Cache; using System.Web.UI.HtmlControls; using System.Xml; + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.Xrds; @@ -30,7 +31,7 @@ namespace DotNetOpenAuth.Yadis { #if DEBUG internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache); #else - internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable); + internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(DotNetOpenAuthSection.Configuration.OpenId.CacheDiscovery ? HttpRequestCacheLevel.CacheIfAvailable : HttpRequestCacheLevel.BypassCache); #endif /// <summary> @@ -80,7 +81,7 @@ namespace DotNetOpenAuth.Yadis { Logger.Yadis.DebugFormat("{0} found in HTTP header. Preparing to pull XRDS from {1}", HeaderName, url); } } - if (url == null && response.ContentType != null && response.ContentType.MediaType == ContentTypes.Html) { + if (url == null && response.ContentType != null && (response.ContentType.MediaType == ContentTypes.Html || response.ContentType.MediaType == ContentTypes.XHtml)) { url = FindYadisDocumentLocationInHtmlMetaTags(response.GetResponseString()); if (url != null) { Logger.Yadis.DebugFormat("{0} found in HTML Http-Equiv tag. Preparing to pull XRDS from {1}", HeaderName, url); |