diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-12-31 12:04:06 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-12-31 12:04:06 -0800 |
commit | 36bb738e4cc937a8b04321ef85b098d16d0793c8 (patch) | |
tree | dba34217c34ec24af4a0c9281dc8ffa7eec4e45f /samples | |
parent | 0a71b4599eb77f774a4d083bc4faa788ebb4e356 (diff) | |
download | DotNetOpenAuth-36bb738e4cc937a8b04321ef85b098d16d0793c8.zip DotNetOpenAuth-36bb738e4cc937a8b04321ef85b098d16d0793c8.tar.gz DotNetOpenAuth-36bb738e4cc937a8b04321ef85b098d16d0793c8.tar.bz2 |
Locked down the OP endpoint authorized to log into the SSO RP. And the OP now asserts the users' roles to the RP.
Diffstat (limited to 'samples')
13 files changed, 268 insertions, 22 deletions
diff --git a/samples/OpenIdWebRingSsoProvider/Code/Util.cs b/samples/OpenIdWebRingSsoProvider/Code/Util.cs index ea01c9f..07064a2 100644 --- a/samples/OpenIdWebRingSsoProvider/Code/Util.cs +++ b/samples/OpenIdWebRingSsoProvider/Code/Util.cs @@ -9,9 +9,12 @@ namespace OpenIdWebRingSsoProvider.Code { using System.Configuration; using System.Web; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.Provider; public class Util { + private const string RolesAttribute = "http://samples.dotnetopenauth.net/sso/roles"; + public static string ExtractUserName(Uri url) { return url.Segments[url.Segments.Length - 1]; } @@ -68,6 +71,16 @@ namespace OpenIdWebRingSsoProvider.Code { if (idrequest.IsAuthenticated.Value) { // add extension responses here. + var fetchRequest = idrequest.GetExtension<FetchRequest>(); + if (fetchRequest != null) { + var fetchResponse = new FetchResponse(); + if (fetchRequest.Attributes.Contains(RolesAttribute)) { + // Inform the RP what roles this user should fill + // These roles would normally come out of the user database. + fetchResponse.Attributes.Add(RolesAttribute, "Member", "Admin"); + } + idrequest.AddResponseExtension(fetchResponse); + } } } } diff --git a/samples/OpenIdWebRingSsoProvider/Default.aspx b/samples/OpenIdWebRingSsoProvider/Default.aspx index 5b74ffb..9bddc98 100644 --- a/samples/OpenIdWebRingSsoProvider/Default.aspx +++ b/samples/OpenIdWebRingSsoProvider/Default.aspx @@ -9,9 +9,17 @@ </head> <body> <form id="form1" runat="server"> - <div> - Provider SSO home page. - </div> + <p> + This sample is of an OpenID Provider that acts within a controlled set of web + sites (perhaps all belonging to the same organization). It authenticates + the user in its own way (Windows Auth, username/password, InfoCard, X.509, + anything), and then sends an automatically OpenID assertion to a limited set of + whitelisted RPs without prompting the user. + </p> + <p> + This particular sample uses Windows Authentication so that when the user visits + an RP and the RP sends the user to this OP for authentication, the process is + completely implicit -- the user never sees the OP.</p> </form> </body> </html> diff --git a/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx new file mode 100644 index 0000000..d3653e7 --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx @@ -0,0 +1,19 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="OpenIdWebRingSsoRelyingParty.Admin.Default" %> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head runat="server"> + <title></title> +</head> +<body> + <form id="form1" runat="server"> + <div> + You must be an admin! + </div> + <p> + The roles you're assigned come from the trusted Provider's identity assertion. The + sample OP comes hard-wired to assert membership in the Admin and Member roles. + </p> + </form> +</body> +</html> diff --git a/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.cs b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.cs new file mode 100644 index 0000000..94da1f7 --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.cs @@ -0,0 +1,13 @@ +namespace OpenIdWebRingSsoRelyingParty.Admin { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + + public partial class Default : System.Web.UI.Page { + protected void Page_Load(object sender, EventArgs e) { + } + } +} diff --git a/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.designer.cs b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.designer.cs new file mode 100644 index 0000000..9519fc3 --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/Admin/Default.aspx.designer.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4927 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace OpenIdWebRingSsoRelyingParty.Admin { + + + public partial class Default { + + /// <summary> + /// form1 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.HtmlControls.HtmlForm form1; + } +} diff --git a/samples/OpenIdWebRingSsoRelyingParty/Admin/Web.config b/samples/OpenIdWebRingSsoRelyingParty/Admin/Web.config new file mode 100644 index 0000000..52a5faf --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/Admin/Web.config @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<configuration> + <system.web> + <authorization> + <allow roles="Admin"/> + <deny users="*"/> + </authorization> + </system.web> +</configuration> diff --git a/samples/OpenIdWebRingSsoRelyingParty/AuthTicketRoles.cs b/samples/OpenIdWebRingSsoRelyingParty/AuthTicketRoles.cs new file mode 100644 index 0000000..06783bd --- /dev/null +++ b/samples/OpenIdWebRingSsoRelyingParty/AuthTicketRoles.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthTicketRoles.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace OpenIdWebRingSsoRelyingParty { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Principal; + using System.Web; + using System.Web.Security; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.OAuth.ChannelElements; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// An authentication module that utilizes the forms auth ticket cookie + /// as a cache for the users' roles, since those roles are determined by + /// the OpenID Provider and we don't have a local user-roles cache at this + /// RP since those relationships are always managed by the Provider. + /// </summary> + public class AuthTicketRoles : IHttpModule { + #region IHttpModule Members + + /// <summary> + /// Initializes a module and prepares it to handle requests. + /// </summary> + /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param> + public void Init(HttpApplication context) { + context.AuthenticateRequest += this.application_AuthenticateRequest; + } + + /// <summary> + /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>. + /// </summary> + public void Dispose() { + } + + #endregion + + private void application_AuthenticateRequest(object sender, EventArgs e) { + if (HttpContext.Current.User != null) { + var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]; + if (cookie != null) { + var ticket = FormsAuthentication.Decrypt(cookie.Value); + if (!string.IsNullOrEmpty(ticket.UserData)) { + string[] roles = ticket.UserData.Split(';'); + HttpContext.Current.User = new GenericPrincipal(HttpContext.Current.User.Identity, roles); + } + } + } + } + } +} diff --git a/samples/OpenIdWebRingSsoRelyingParty/Default.aspx b/samples/OpenIdWebRingSsoRelyingParty/Default.aspx index 017ff8d..00efb08 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/Default.aspx +++ b/samples/OpenIdWebRingSsoRelyingParty/Default.aspx @@ -8,8 +8,22 @@ <body> <form id="form1" runat="server"> <div> + We've recognized you (via the SSO OP) as: <asp:LoginName ID="LoginName1" runat="server" /> + <p>Try visiting the <a href="Admin/Default.aspx">Admin area</a></p> </div> + <p>This sample is of an OpenID Relying Party that acts within a controlled set of + web sites (perhaps all belonging to the same organization). This + particular RP is configured to require authentication for all web pages, and to + always use just one (trusted) OP (the OpenIdWebRingSsoProvider) without ever + prompting the user.</p> + <p>Although the sample OP uses Windows Authentication, and so this RP could easily + do the same, the idea is that the OP and RP may exist on different network + topologies, or the OP may be the only site with access to the user credential + database, or any number of other scenarios where the RP doesn't have the freedom + to authenticate the user the way the OP has, yet this set of web sites want to + have the users only authenticate themselves to one site with one set of + credentials.</p> </form> </body> </html> diff --git a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx index ab97e6a..2e7df2e 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx +++ b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx @@ -7,13 +7,20 @@ </head> <body> <form id="form1" runat="server"> - <div> - Sorry. We couldn't log you in. - </div> - <asp:Label runat="server" ID="errorLabel" /> - <p> - <asp:Button ID="retryButton" runat="server" Text="Try Again" OnClick="retryButton_Click" /> - </p> + <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0"> + <asp:View ID="View1" runat="server"> + <div> + Sorry. We couldn't log you in. + </div> + <asp:Label runat="server" ID="errorLabel" /> + <p> + <asp:Button ID="retryButton" runat="server" Text="Try Again" OnClick="retryButton_Click" /> + </p> + </asp:View> + <asp:View ID="View2" runat="server"> + You don't have permission to visit <%=HttpUtility.HtmlEncode(Request.QueryString["ReturnUrl"]) %>. + </asp:View> + </asp:MultiView> </form> </body> </html> diff --git a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.cs b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.cs index e955b31..ec5af08 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.cs +++ b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.cs @@ -8,11 +8,19 @@ using System.Web.UI; using System.Web.UI.WebControls; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.RelyingParty; public partial class Login : System.Web.UI.Page { + private const string RolesAttribute = "http://samples.dotnetopenauth.net/sso/roles"; + private static OpenIdRelyingParty relyingParty = new OpenIdRelyingParty(); + static Login() { + // Configure the RP to only allow assertions from our trusted OP endpoint. + relyingParty.EndpointFilter = ep => ep.Uri.AbsoluteUri == ConfigurationManager.AppSettings["SsoProviderOPEndpoint"]; + } + protected void Page_Load(object sender, EventArgs e) { UriBuilder returnToBuilder = new UriBuilder(Request.Url); returnToBuilder.Path = "/login.aspx"; @@ -24,24 +32,56 @@ var response = relyingParty.GetResponse(); if (response == null) { - // Because this is a sample of a controlled SSO environment, - // we don't ask the user which Provider to use... we just send - // them straight off to the one Provider we trust. - var request = relyingParty.CreateRequest( - ConfigurationManager.AppSettings["SsoProvider"], - realm, - returnTo); - request.RedirectToProvider(); + if (Request.QueryString["ReturnUrl"] != null && User.Identity.IsAuthenticated) { + // The user must have been directed here because he has insufficient + // permissions to access something. + MultiView1.ActiveViewIndex = 1; + } else { + // Because this is a sample of a controlled SSO environment, + // we don't ask the user which Provider to use... we just send + // them straight off to the one Provider we trust. + var request = relyingParty.CreateRequest( + ConfigurationManager.AppSettings["SsoProviderOPIdentifier"], + realm, + returnTo); + var fetchRequest = new FetchRequest(); + fetchRequest.Attributes.AddOptional(RolesAttribute); + request.AddExtension(fetchRequest); + request.RedirectToProvider(); + } } else { switch (response.Status) { case AuthenticationStatus.Canceled: - errorLabel.Text = "Login canceled."; + this.errorLabel.Text = "Login canceled."; break; case AuthenticationStatus.Failed: - errorLabel.Text = HttpUtility.HtmlEncode(response.Exception.Message); + this.errorLabel.Text = HttpUtility.HtmlEncode(response.Exception.Message); break; case AuthenticationStatus.Authenticated: - FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, false); + IList<string> roles = null; + var fetchResponse = response.GetExtension<FetchResponse>(); + if (fetchResponse != null) { + if (fetchResponse.Attributes.Contains(RolesAttribute)) { + roles = fetchResponse.Attributes[RolesAttribute].Values; + } + } + if (roles == null) { + roles = new List<string>(0); + } + + // Apply the roles to this auth ticket + const int TimeoutInMinutes = 100; // TODO: look up the right value from the web.config file + var ticket = new FormsAuthenticationTicket( + 2, + response.ClaimedIdentifier, + DateTime.Now, + DateTime.Now.AddMinutes(TimeoutInMinutes), + false, // non-persistent, since login is automatic and we wanted updated roles + string.Join(";", roles.ToArray())); + + HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)); + Response.SetCookie(cookie); + Response.Redirect(Request.QueryString["ReturnUrl"] ?? FormsAuthentication.DefaultUrl); break; default: break; diff --git a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.designer.cs b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.designer.cs index a413966..7ed2669 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.designer.cs +++ b/samples/OpenIdWebRingSsoRelyingParty/Login.aspx.designer.cs @@ -23,6 +23,24 @@ namespace OpenIdWebRingSsoRelyingParty { protected global::System.Web.UI.HtmlControls.HtmlForm form1; /// <summary> + /// MultiView1 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.MultiView MultiView1; + + /// <summary> + /// View1 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.View View1; + + /// <summary> /// errorLabel control. /// </summary> /// <remarks> @@ -39,5 +57,14 @@ namespace OpenIdWebRingSsoRelyingParty { /// To modify move field declaration from designer file to code-behind file. /// </remarks> protected global::System.Web.UI.WebControls.Button retryButton; + + /// <summary> + /// View2 control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.View View2; } } diff --git a/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj b/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj index 3cc9d44..978a1a57 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj +++ b/samples/OpenIdWebRingSsoRelyingParty/OpenIdWebRingSsoRelyingParty.csproj @@ -59,6 +59,13 @@ <Content Include="xrds.aspx" /> </ItemGroup> <ItemGroup> + <Compile Include="Admin\Default.aspx.cs"> + <DependentUpon>Default.aspx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="Admin\Default.aspx.designer.cs"> + <DependentUpon>Default.aspx</DependentUpon> + </Compile> <Compile Include="Default.aspx.cs"> <SubType>ASPXCodeBehind</SubType> <DependentUpon>Default.aspx</DependentUpon> @@ -73,6 +80,7 @@ <Compile Include="Login.aspx.designer.cs"> <DependentUpon>Login.aspx</DependentUpon> </Compile> + <Compile Include="AuthTicketRoles.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <ItemGroup> @@ -82,6 +90,10 @@ </ProjectReference> </ItemGroup> <ItemGroup> + <Content Include="Admin\Default.aspx" /> + <Content Include="Admin\Web.config" /> + </ItemGroup> + <ItemGroup> <Folder Include="App_Data\" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> diff --git a/samples/OpenIdWebRingSsoRelyingParty/Web.config b/samples/OpenIdWebRingSsoRelyingParty/Web.config index 6c5ca43..94ef60c 100644 --- a/samples/OpenIdWebRingSsoRelyingParty/Web.config +++ b/samples/OpenIdWebRingSsoRelyingParty/Web.config @@ -64,7 +64,8 @@ </dotNetOpenAuth> <appSettings> - <add key="SsoProvider" value="http://localhost:39167/" /> + <add key="SsoProviderOPIdentifier" value="http://localhost:39167/" /> + <add key="SsoProviderOPEndpoint" value="http://localhost:39167/server.aspx" /> </appSettings> <connectionStrings/> @@ -124,6 +125,7 @@ </httpHandlers> <httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> + <add name="AuthTicketRoles" type="OpenIdWebRingSsoRelyingParty.AuthTicketRoles, OpenIdWebRingSsoRelyingParty"/> </httpModules> </system.web> |