1
//-----------------------------------------------------------------------
2 // <copyright file="OpenIdProvider.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth
.OpenId
.Provider
{
9 using System
.ComponentModel
;
12 using DotNetOpenAuth
.Configuration
;
13 using DotNetOpenAuth
.Messaging
;
14 using DotNetOpenAuth
.Messaging
.Bindings
;
15 using DotNetOpenAuth
.OpenId
.ChannelElements
;
16 using DotNetOpenAuth
.OpenId
.Messages
;
19 /// Offers services for a web page that is acting as an OpenID identity server.
21 public sealed class OpenIdProvider
{
23 /// The name of the key to use in the HttpApplication cache to store the
24 /// instance of <see cref="StandardProviderApplicationStore"/> to use.
26 private const string ApplicationStoreKey
= "DotNetOpenAuth.OpenId.Provider.OpenIdProvider.ApplicationStore";
29 /// Backing field for the <see cref="SecuritySettings"/> property.
31 private ProviderSecuritySettings securitySettings
;
34 /// Initializes a new instance of the <see cref="OpenIdProvider"/> class.
36 public OpenIdProvider()
37 : this(DotNetOpenAuthSection
.Configuration
.OpenId
.Provider
.ApplicationStore
.CreateInstance(HttpApplicationStore
)) {
41 /// Initializes a new instance of the <see cref="OpenIdProvider"/> class.
43 /// <param name="applicationStore">The application store to use. Cannot be null.</param>
44 public OpenIdProvider(IProviderApplicationStore applicationStore
)
45 : this(applicationStore
, applicationStore
) {
49 /// Initializes a new instance of the <see cref="OpenIdProvider"/> class.
51 /// <param name="associationStore">The association store to use. Cannot be null.</param>
52 /// <param name="nonceStore">The nonce store to use. Cannot be null.</param>
53 private OpenIdProvider(IAssociationStore
<AssociationRelyingPartyType
> associationStore
, INonceStore nonceStore
) {
54 ErrorUtilities
.VerifyArgumentNotNull(associationStore
, "associationStore");
55 ErrorUtilities
.VerifyArgumentNotNull(nonceStore
, "nonceStore");
57 this.AssociationStore
= associationStore
;
58 this.SecuritySettings
= DotNetOpenAuthSection
.Configuration
.OpenId
.Provider
.SecuritySettings
.CreateSecuritySettings();
59 this.Channel
= new OpenIdChannel(this.AssociationStore
, nonceStore
, this.SecuritySettings
);
63 /// Gets the standard state storage mechanism that uses ASP.NET's
64 /// HttpApplication state dictionary to store associations and nonces.
66 [EditorBrowsable(EditorBrowsableState
.Advanced
)]
67 public static IProviderApplicationStore HttpApplicationStore
{
69 HttpContext context
= HttpContext
.Current
;
70 ErrorUtilities
.VerifyOperation(context
!= null, OpenIdStrings
.StoreRequiredWhenNoHttpContextAvailable
, typeof(IProviderApplicationStore
).Name
);
71 var store
= (IProviderApplicationStore
)context
.Application
[ApplicationStoreKey
];
73 context
.Application
.Lock();
75 if ((store
= (IProviderApplicationStore
)context
.Application
[ApplicationStoreKey
]) == null) {
76 context
.Application
[ApplicationStoreKey
] = store
= new StandardProviderApplicationStore();
79 context
.Application
.UnLock();
88 /// Gets the channel to use for sending/receiving messages.
90 public Channel Channel { get; internal set; }
93 /// Gets the security settings used by this Provider.
95 public ProviderSecuritySettings SecuritySettings
{
97 return this.securitySettings
;
101 ErrorUtilities
.VerifyArgumentNotNull(value, "value");
102 this.securitySettings
= value;
107 /// Gets the association store.
109 internal IAssociationStore
<AssociationRelyingPartyType
> AssociationStore { get; private set; }
112 /// Gets the web request handler to use for discovery and the part of
113 /// authentication where direct messages are sent to an untrusted remote party.
115 internal IDirectWebRequestHandler WebRequestHandler
{
116 get { return this.Channel.WebRequestHandler; }
120 /// Gets the incoming OpenID request if there is one, or null if none was detected.
122 /// <returns>The request that the hosting Provider should possibly process and then transmit the response for.</returns>
124 /// <para>Requests may be infrastructural to OpenID and allow auto-responses, or they may
125 /// be authentication requests where the Provider site has to make decisions based
126 /// on its own user database and policies.</para>
127 /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
129 /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
130 /// <exception cref="ProtocolException">Thrown if the incoming message is recognized but deviates from the protocol specification irrecoverably.</exception>
131 public IRequest
GetRequest() {
132 return this.GetRequest(this.Channel
.GetRequestFromContext());
136 /// Gets the incoming OpenID request if there is one, or null if none was detected.
138 /// <param name="httpRequestInfo">The incoming HTTP request to extract the message from.</param>
140 /// The request that the hosting Provider should process and then transmit the response for.
141 /// Null if no valid OpenID request was detected in the given HTTP request.
144 /// Requests may be infrastructural to OpenID and allow auto-responses, or they may
145 /// be authentication requests where the Provider site has to make decisions based
146 /// on its own user database and policies.
148 /// <exception cref="ProtocolException">Thrown if the incoming message is recognized but deviates from the protocol specification irrecoverably.</exception>
149 public IRequest
GetRequest(HttpRequestInfo httpRequestInfo
) {
150 ErrorUtilities
.VerifyArgumentNotNull(httpRequestInfo
, "httpRequestInfo");
152 IDirectedProtocolMessage incomingMessage
= this.Channel
.ReadFromRequest(httpRequestInfo
);
153 if (incomingMessage
== null) {
157 var checkIdMessage
= incomingMessage
as CheckIdRequest
;
158 if (checkIdMessage
!= null) {
159 return new AuthenticationRequest(this, checkIdMessage
);
162 var checkAuthMessage
= incomingMessage
as CheckAuthenticationRequest
;
163 if (checkAuthMessage
!= null) {
164 return new AutoResponsiveRequest(this, incomingMessage
, new CheckAuthenticationResponse(checkAuthMessage
, this));
167 var associateMessage
= incomingMessage
as AssociateRequest
;
168 if (associateMessage
!= null) {
169 return new AutoResponsiveRequest(this, incomingMessage
, associateMessage
.CreateResponse(this.AssociationStore
, this.SecuritySettings
));
172 throw ErrorUtilities
.ThrowProtocol(MessagingStrings
.UnexpectedMessageReceivedOfMany
);
176 /// Send an identity assertion on behalf of one of this Provider's
177 /// members in order to redirect the user agent to a relying party
178 /// web site and log him/her in immediately in one uninterrupted step.
180 /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
181 /// <param name="relyingParty">The URL of the Relying Party web site.
182 /// This will typically be the home page, but may be a longer URL if
183 /// that Relying Party considers the scope of its realm to be more specific.
184 /// The URL provided here must allow discovery of the Relying Party's
185 /// XRDS document that advertises its OpenID RP endpoint.</param>
186 /// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
187 /// <param name="localIdentifier">The Identifier you know your user by internally. This will typically
188 /// be the same as <paramref name="claimedIdentifier"/>.</param>
189 /// <param name="extensions">The extensions.</param>
191 /// A <see cref="UserAgentResponse"/> object describing the HTTP response to send
192 /// the user agent to allow the redirect with assertion to happen.
194 public UserAgentResponse
PrepareUnsolicitedAssertion(Uri providerEndpoint
, Realm relyingParty
, Identifier claimedIdentifier
, Identifier localIdentifier
, params IExtensionMessage
[] extensions
) {
195 ErrorUtilities
.VerifyArgumentNotNull(providerEndpoint
, "providerEndpoint");
196 ErrorUtilities
.VerifyArgumentNotNull(relyingParty
, "relyingParty");
197 ErrorUtilities
.VerifyArgumentNotNull(claimedIdentifier
, "claimedIdentifier");
198 ErrorUtilities
.VerifyArgumentNotNull(localIdentifier
, "localIdentifier");
199 ErrorUtilities
.VerifyArgumentNamed(providerEndpoint
.IsAbsoluteUri
, "providerEndpoint", OpenIdStrings
.AbsoluteUriRequired
);
201 // Although the RP should do their due diligence to make sure that this OP
202 // is authorized to send an assertion for the given claimed identifier,
203 // do due diligence by performing our own discovery on the claimed identifier
204 // and make sure that it is tied to this OP and OP local identifier.
207 Logger
.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier
);
208 var returnToEndpoint
= relyingParty
.Discover(this.WebRequestHandler
, true).FirstOrDefault();
209 ErrorUtilities
.VerifyProtocol(returnToEndpoint
!= null, OpenIdStrings
.NoRelyingPartyEndpointDiscovered
, relyingParty
);
211 var positiveAssertion
= new PositiveAssertionResponse(returnToEndpoint
) {
212 ProviderEndpoint
= providerEndpoint
,
213 ClaimedIdentifier
= claimedIdentifier
,
214 LocalIdentifier
= localIdentifier
,
217 if (extensions
!= null) {
218 foreach (IExtensionMessage extension
in extensions
) {
219 positiveAssertion
.Extensions
.Add(extension
);
223 return this.Channel
.PrepareResponse(positiveAssertion
);