summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/OpenIdProviderMvc/Controllers/OpenIdController.cs191
-rw-r--r--samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj1
-rw-r--r--samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx38
-rw-r--r--samples/OpenIdProviderMvc/Web.config1
4 files changed, 200 insertions, 31 deletions
diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs
index bd0fdbf..0ef9680 100644
--- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs
+++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs
@@ -9,63 +9,192 @@ namespace OpenIdProviderMvc.Controllers {
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Behaviors;
using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.Provider;
using OpenIdProviderMvc.Code;
public class OpenIdController : Controller {
internal static OpenIdProvider OpenIdProvider = new OpenIdProvider();
- internal static IAuthenticationRequest PendingAuthenticationRequest {
- get { return ProviderEndpoint.PendingAuthenticationRequest; }
- set { ProviderEndpoint.PendingAuthenticationRequest = value; }
- }
-
[ValidateInput(false)]
public ActionResult Provider() {
IRequest request = OpenIdProvider.GetRequest();
if (request != null) {
- var authRequest = request as IAuthenticationRequest;
- if (authRequest != null) {
- PendingAuthenticationRequest = authRequest;
- if (authRequest.IsReturnUrlDiscoverable(OpenIdProvider) == RelyingPartyDiscoveryResult.Success &&
- User.Identity.IsAuthenticated &&
- (authRequest.IsDirectedIdentity || this.UserControlsIdentifier(authRequest))) {
- return this.SendAssertion();
- } else {
- return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion") });
- }
- }
-
+ // Some requests are automatically handled by DotNetOpenAuth. If this is one, go ahead and let it go.
if (request.IsResponseReady) {
return OpenIdProvider.PrepareResponse(request).AsActionResult();
- } else {
- return RedirectToAction("LogOn", "Account");
}
+
+ // This is apparently one that the host (the web site itself) has to respond to.
+ ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request;
+
+ // Try responding immediately if possible.
+ ActionResult response;
+ if (this.AutoRespondIfPossible(out response)) {
+ return response;
+ }
+
+ // We can't respond immediately with a positive result. But if we still have to respond immediately...
+ if (ProviderEndpoint.PendingRequest.Immediate) {
+ // We can't stop to prompt the user -- we must just return a negative response.
+ return this.SendAssertion();
+ }
+
+ return this.RedirectToAction("AskUser");
} else {
- return View();
+ // No OpenID request was recognized. This may be a user that stumbled on the OP Endpoint.
+ return this.View();
}
}
+ /// <summary>
+ /// Displays a confirmation page.
+ /// </summary>
[Authorize]
+ public ActionResult AskUser() {
+ if (ProviderEndpoint.PendingRequest == null) {
+ // Oops... precious little we can confirm without a pending OpenID request.
+ return this.RedirectToAction("Index", "Home");
+ }
+
+ // The user MAY have just logged in. Try again to respond automatically to the RP if appropriate.
+ ActionResult response;
+ if (this.AutoRespondIfPossible(out response)) {
+ return response;
+ }
+
+ this.ViewData["Realm"] = ProviderEndpoint.PendingRequest.Realm;
+
+ return this.View();
+ }
+
+ [HttpPost, Authorize, ValidateAntiForgeryToken]
+ public ActionResult AskUserResponse(bool confirmed) {
+ if (ProviderEndpoint.PendingAnonymousRequest != null) {
+ ProviderEndpoint.PendingAnonymousRequest.IsApproved = confirmed;
+ } else if (ProviderEndpoint.PendingAuthenticationRequest != null) {
+ ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = confirmed;
+ } else {
+ throw new InvalidOperationException("There's no pending authentication request!");
+ }
+
+ return this.SendAssertion();
+ }
+
+ /// <summary>
+ /// Sends a positive or a negative assertion, based on how the pending request is currently marked.
+ /// </summary>
+ /// <returns>An MVC redirect result.</returns>
public ActionResult SendAssertion() {
- IAuthenticationRequest authReq = PendingAuthenticationRequest;
- PendingAuthenticationRequest = null; // clear session static so we don't do this again
- if (authReq == null) {
+ var pendingRequest = ProviderEndpoint.PendingRequest;
+ var authReq = pendingRequest as IAuthenticationRequest;
+ var anonReq = pendingRequest as IAnonymousRequest;
+ ProviderEndpoint.PendingRequest = null; // clear session static so we don't do this again
+ if (pendingRequest == null) {
throw new InvalidOperationException("There's no pending authentication request!");
}
- if (authReq.IsDirectedIdentity) {
- authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name);
+ // Set safe defaults if somehow the user ended up (perhaps through XSRF) here before electing to send data to the RP.
+ if (anonReq != null && !anonReq.IsApproved.HasValue) {
+ anonReq.IsApproved = false;
}
- if (!authReq.IsDelegatedIdentifier) {
- authReq.ClaimedIdentifier = authReq.LocalIdentifier;
+
+ if (authReq != null && !authReq.IsAuthenticated.HasValue) {
+ authReq.IsAuthenticated = false;
}
- // Respond to AX/sreg extension requests.
- //// Real web sites would have code here
+ if (authReq != null && authReq.IsAuthenticated.Value) {
+ if (authReq.IsDirectedIdentity) {
+ authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name);
+ }
+
+ if (!authReq.IsDelegatedIdentifier) {
+ authReq.ClaimedIdentifier = authReq.LocalIdentifier;
+ }
+ }
+
+ // Respond to AX/sreg extension requests only on a positive result.
+ if ((authReq != null && authReq.IsAuthenticated.Value) ||
+ (anonReq != null && anonReq.IsApproved.Value)) {
+ // Look for a Simple Registration request. When the AXFetchAsSregTransform behavior is turned on
+ // in the web.config file as it is in this sample, AX requests will come in as SReg requests.
+ var claimsRequest = pendingRequest.GetExtension<ClaimsRequest>();
+ if (claimsRequest != null) {
+ var claimsResponse = claimsRequest.CreateResponse();
+
+ // This simple respond to a request check may be enhanced to only respond to an individual attribute
+ // request if the user consents to it explicitly, in which case this response extension creation can take
+ // place in the confirmation page action rather than here.
+ if (claimsRequest.Email != DemandLevel.NoRequest) {
+ claimsResponse.Email = User.Identity.Name + "@dotnetopenauth.net";
+ }
+
+ pendingRequest.AddResponseExtension(claimsResponse);
+ }
+ }
+
+ return OpenIdProvider.PrepareResponse(pendingRequest).AsActionResult();
+ }
+
+ /// <summary>
+ /// Attempts to formulate an automatic response to the RP if the user's profile allows it.
+ /// </summary>
+ /// <param name="response">Receives the ActionResult for the caller to return, or <c>null</c> if no automatic response can be made.</param>
+ /// <returns>A value indicating whether an automatic response is possible.</returns>
+ private bool AutoRespondIfPossible(out ActionResult response) {
+ // If the odds are good we can respond to this one immediately (without prompting the user)...
+ if (ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(OpenIdProvider) == RelyingPartyDiscoveryResult.Success
+ && User.Identity.IsAuthenticated
+ && this.HasUserAuthorizedAutoLogin(ProviderEndpoint.PendingRequest)) {
+ // Is this is an identity authentication request? (as opposed to an anonymous request)...
+ if (ProviderEndpoint.PendingAuthenticationRequest != null) {
+ // If this is directed identity, or if the claimed identifier being checked is controlled by the current user...
+ if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity
+ || this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) {
+ ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true;
+ response = this.SendAssertion();
+ return true;
+ }
+ }
+
+ // If this is an anonymous request, we can respond to that too.
+ if (ProviderEndpoint.PendingAnonymousRequest != null) {
+ ProviderEndpoint.PendingAnonymousRequest.IsApproved = true;
+ response = this.SendAssertion();
+ return true;
+ }
+ }
+
+ response = null;
+ return false;
+ }
+
+ /// <summary>
+ /// Determines whether the currently logged in user has authorized auto login to the requesting relying party.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if it is safe to respond affirmatively to this request and all extensions
+ /// without further user confirmation; otherwise, <c>false</c>.
+ /// </returns>
+ private bool HasUserAuthorizedAutoLogin(IHostProcessedRequest request) {
+ // TODO: host should implement this method meaningfully, consulting their user database.
+ // Make sure the user likes the RP
+ if (true/*User.UserLikesRP(request.Realm))*/) {
+ // And make sure the RP is only asking for information about the user that the user has granted before.
+ if (true/*User.HasGrantedExtensions(request)*/) {
+ // For now for the purposes of the sample, we'll disallow auto-logins when an sreg request is present.
+ if (request.GetExtension<ClaimsRequest>() != null) {
+ return false;
+ }
+
+ return true;
+ }
+ }
- authReq.IsAuthenticated = this.UserControlsIdentifier(authReq);
- return OpenIdProvider.PrepareResponse(authReq).AsActionResult();
+ // If we aren't sure the user likes this site and is willing to disclose the requested info, return false
+ // so the user has the opportunity to explicity choose whether to share his/her info.
+ return false;
}
/// <summary>
diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
index 794a91e..9dc060e 100644
--- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
+++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj
@@ -92,6 +92,7 @@
<Content Include="Views\Account\ChangePassword.aspx" />
<Content Include="Views\Account\ChangePasswordSuccess.aspx" />
<Content Include="Views\Account\Register.aspx" />
+ <Content Include="Views\OpenId\AskUser.aspx" />
<Content Include="Views\Shared\Xrds.aspx" />
<Content Include="Views\OpenId\Provider.aspx" />
<Content Include="Views\User\Identity.aspx" />
diff --git a/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx b/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx
new file mode 100644
index 0000000..5098325
--- /dev/null
+++ b/samples/OpenIdProviderMvc/Views/OpenId/AskUser.aspx
@@ -0,0 +1,38 @@
+<%@ Page Title="Do you want to log into another web site?" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
+ Inherits="System.Web.Mvc.ViewPage" %>
+
+<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
+ <h2>
+ Logging in somewhere?
+ </h2>
+ <p>
+ Are you trying to log into
+ <b><%= Html.Encode(ViewData["Realm"]) %></b>?
+ </p>
+ <% using (Html.BeginForm("AskUserResponse", "OpenId")) { %>
+ <%= Html.AntiForgeryToken() %>
+ <%= Html.Hidden("confirmed", "false") %>
+ <div style="display: none" id="responseButtonsDiv">
+ <input type="submit" value="yes" onclick="document.getElementsByName('confirmed')[0].value = 'true'; return true;" />
+ <input type="submit" value="no" />
+ </div>
+ <div id="javascriptDisabled">
+ <b>Javascript appears to be disabled in your browser. </b>This page requires Javascript
+ to be enabled to better protect your security.
+ </div>
+ <script language="javascript" type="text/javascript">
+ //<![CDATA[
+ // we use HTML to hide the action buttons and Javascript to show them
+ // to protect against click-jacking in an iframe whose javascript is disabled.
+ document.getElementById('responseButtonsDiv').style.display = 'block';
+ document.getElementById('javascriptDisabled').style.display = 'none';
+
+ // Frame busting code (to protect us from being hosted in an iframe).
+ // This protects us from click-jacking.
+ if (document.location !== window.top.location) {
+ window.top.location = document.location;
+ }
+ //]]>
+ </script>
+ <% } %>
+</asp:Content>
diff --git a/samples/OpenIdProviderMvc/Web.config b/samples/OpenIdProviderMvc/Web.config
index cc30638..9b1eb90 100644
--- a/samples/OpenIdProviderMvc/Web.config
+++ b/samples/OpenIdProviderMvc/Web.config
@@ -46,6 +46,7 @@
profile matches, the default behavior is assumed. -->
<!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />-->
<add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" />
+ <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" />
</behaviors>
<!-- Uncomment the following to activate the sample custom store. -->
<!--<store type="RelyingPartyWebForms.CustomStore, RelyingPartyWebForms" />-->