diff options
Diffstat (limited to 'samples')
48 files changed, 745 insertions, 582 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 570d91f..976a325 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -59,11 +59,7 @@ <Compile Include="CustomExtensions\AcmeRequest.cs" /> <Compile Include="CustomExtensions\AcmeResponse.cs" /> <Compile Include="GoogleConsumer.cs" /> - <Compile Include="OAuthIdentity.cs" /> - <Compile Include="OAuthPrincipal.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Provider\AnonymousIdentifierProviderBase.cs" /> - <Compile Include="Provider\AuthenticationRequestExtensions.cs" /> <Compile Include="TwitterConsumer.cs" /> <Compile Include="Util.cs" /> </ItemGroup> 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/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs b/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs deleted file mode 100644 index ea9ec0b..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/OAuthIdentity.cs +++ /dev/null @@ -1,63 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthIdentity.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock { - using System; - using System.Runtime.InteropServices; - using System.Security.Principal; - - /// <summary> - /// Represents an OAuth consumer that is impersonating a known user on the system. - /// </summary> - [Serializable] - [ComVisible(true)] - internal class OAuthIdentity : IIdentity { - /// <summary> - /// Initializes a new instance of the <see cref="OAuthIdentity"/> class. - /// </summary> - /// <param name="username">The username.</param> - internal OAuthIdentity(string username) { - if (String.IsNullOrEmpty(username)) { - throw new ArgumentNullException("username"); - } - - this.Name = username; - } - - #region IIdentity Members - - /// <summary> - /// Gets the type of authentication used. - /// </summary> - /// <value>The constant "OAuth"</value> - /// <returns> - /// The type of authentication used to identify the user. - /// </returns> - public string AuthenticationType { - get { return "OAuth"; } - } - - /// <summary> - /// Gets a value indicating whether the user has been authenticated. - /// </summary> - /// <value>The value <c>true</c></value> - /// <returns>true if the user was authenticated; otherwise, false. - /// </returns> - public bool IsAuthenticated { - get { return true; } - } - - /// <summary> - /// Gets the name of the user who authorized the OAuth token the consumer is using for authorization. - /// </summary> - /// <returns> - /// The name of the user on whose behalf the code is running. - /// </returns> - public string Name { get; private set; } - - #endregion - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs b/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs deleted file mode 100644 index 88f3b83..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/OAuthPrincipal.cs +++ /dev/null @@ -1,67 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthPrincipal.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock { - using System; - using System.Linq; - using System.Runtime.InteropServices; - using System.Security.Principal; - - /// <summary> - /// Represents an OAuth consumer that is impersonating a known user on the system. - /// </summary> - [Serializable] - [ComVisible(true)] - internal class OAuthPrincipal : IPrincipal { - /// <summary> - /// The roles this user belongs to. - /// </summary> - private string[] roles; - - /// <summary> - /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. - /// </summary> - /// <param name="identity">The identity.</param> - /// <param name="roles">The roles this user belongs to.</param> - internal OAuthPrincipal(OAuthIdentity identity, string[] roles) { - this.Identity = identity; - this.roles = roles; - } - - /// <summary> - /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. - /// </summary> - /// <param name="username">The username.</param> - /// <param name="roles">The roles this user belongs to.</param> - internal OAuthPrincipal(string username, string[] roles) - : this(new OAuthIdentity(username), roles) { - } - - #region IPrincipal Members - - /// <summary> - /// Gets the identity of the current principal. - /// </summary> - /// <value></value> - /// <returns> - /// The <see cref="T:System.Security.Principal.IIdentity"/> object associated with the current principal. - /// </returns> - public IIdentity Identity { get; private set; } - - /// <summary> - /// Determines whether the current principal belongs to the specified role. - /// </summary> - /// <param name="role">The name of the role for which to check membership.</param> - /// <returns> - /// true if the current principal is a member of the specified role; otherwise, false. - /// </returns> - public bool IsInRole(string role) { - return this.roles.Contains(role); - } - - #endregion - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs deleted file mode 100644 index 1df7267..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AnonymousIdentifierProviderBase.cs +++ /dev/null @@ -1,122 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AnonymousIdentifierProviderBase.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock.Provider { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OpenId; - - public abstract class AnonymousIdentifierProviderBase { - private int newSaltLength = 20; - - /// <summary> - /// Initializes a new instance of the <see cref="AnonymousIdentifierProviderBase"/> class. - /// </summary> - /// <param name="baseIdentifier">The base URI on which to append the anonymous part.</param> - public AnonymousIdentifierProviderBase(Uri baseIdentifier) { - if (baseIdentifier == null) { - throw new ArgumentNullException("baseIdentifier"); - } - - this.Hasher = HashAlgorithm.Create("SHA256"); - this.Encoder = Encoding.UTF8; - this.BaseIdentifier = baseIdentifier; - } - - public Uri BaseIdentifier { get; private set; } - - protected HashAlgorithm Hasher { get; private set; } - - protected Encoding Encoder { get; private set; } - - protected int NewSaltLength { - get { - return this.newSaltLength; - } - - set { - if (value <= 0) { - throw new ArgumentOutOfRangeException("value"); - } - - this.newSaltLength = value; - } - } - - #region IAnonymousIdentifierProvider Members - - public Uri GetAnonymousIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) { - byte[] salt = this.GetHashSaltForLocalIdentifier(localIdentifier); - string valueToHash = localIdentifier + "#" + (relyingPartyRealm ?? string.Empty); - byte[] valueAsBytes = this.Encoder.GetBytes(valueToHash); - byte[] bytesToHash = new byte[valueAsBytes.Length + salt.Length]; - valueAsBytes.CopyTo(bytesToHash, 0); - salt.CopyTo(bytesToHash, valueAsBytes.Length); - byte[] hash = this.Hasher.ComputeHash(bytesToHash); - string base64Hash = Convert.ToBase64String(hash); - Uri anonymousIdentifier = this.AppendIdentifiers(this.BaseIdentifier, base64Hash); - return anonymousIdentifier; - } - - #endregion - - protected virtual byte[] GetNewSalt() { - // We COULD use a crypto random function, but for a salt it seems overkill. - return Util.GetNonCryptoRandomData(this.NewSaltLength); - } - - protected Uri AppendIdentifiers(Uri baseIdentifier, string uriHash) { - if (baseIdentifier == null) { - throw new ArgumentNullException("baseIdentifier"); - } - if (String.IsNullOrEmpty(uriHash)) { - throw new ArgumentNullException("uriHash"); - } - - if (string.IsNullOrEmpty(baseIdentifier.Query)) { - // The uriHash will appear on the path itself. - string pathEncoded = Uri.EscapeUriString(uriHash.Replace('/', '_')); - return new Uri(baseIdentifier, pathEncoded); - } else { - // The uriHash will appear on the query string. - string dataEncoded = Uri.EscapeDataString(uriHash); - return new Uri(baseIdentifier + dataEncoded); - } - } - - /// <summary> - /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier. - /// </summary> - /// <param name="localIdentifier">The OP local identifier.</param> - /// <returns>The salt to use in the hash.</returns> - /// <remarks> - /// It is important that this method always return the same value for a given - /// <paramref name="localIdentifier"/>. - /// New salts can be generated for local identifiers without previously assigned salt - /// values by calling <see cref="GetNewSalt"/> or by a custom method. - /// </remarks> - protected abstract byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier); - -#if CONTRACTS_FULL - /// <summary> - /// Verifies conditions that should be true for any valid state of this object. - /// </summary> - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] - [ContractInvariantMethod] - protected void ObjectInvariant() { - Contract.Invariant(this.Hasher != null); - Contract.Invariant(this.Encoder != null); - Contract.Invariant(this.BaseIdentifier != null); - Contract.Invariant(this.NewHashLength > 0); - } -#endif - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs b/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs deleted file mode 100644 index a737d30..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/Provider/AuthenticationRequestExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace DotNetOpenAuth.ApplicationBlock.Provider { - using System; - using DotNetOpenAuth.OpenId; - using DotNetOpenAuth.OpenId.Provider; - - public static class AuthenticationRequestExtensions { - /// <summary> - /// Removes all personally identifiable information from the positive assertion. - /// </summary> - /// <param name="request">The incoming authentication request.</param> - /// <param name="localIdentifier">The OP local identifier, before the anonymous hash is applied to it.</param> - /// <param name="anonymousIdentifierProvider">The anonymous identifier provider.</param> - /// <param name="pairwiseUnique">if set to <c>true</c> the anonymous identifier will be unique to the requesting relying party's realm.</param> - /// <remarks> - /// The openid.claimed_id and openid.identity values are hashed. - /// </remarks> - public static void ScrubPersonallyIdentifiableInformation(this IAuthenticationRequest request, Identifier localIdentifier, AnonymousIdentifierProviderBase anonymousIdentifierProvider, bool pairwiseUnique) { - if (request == null) { - throw new ArgumentNullException("request"); - } - if (!request.IsDirectedIdentity) { - throw new InvalidOperationException("This operation is supported only under identifier select (directed identity) scenarios."); - } - if (anonymousIdentifierProvider == null) { - throw new ArgumentNullException("anonymousIdentifierProvider"); - } - if (localIdentifier == null) { - throw new ArgumentNullException("localIdentifier"); - } - - // When generating the anonymous identifiers, the openid.identity and openid.claimed_id - // will always end up with matching values. - var anonymousIdentifier = anonymousIdentifierProvider.GetAnonymousIdentifier(localIdentifier, pairwiseUnique ? request.Realm : null); - request.ClaimedIdentifier = anonymousIdentifier; - request.LocalIdentifier = anonymousIdentifier; - } - } -} diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs index 8a188ac..ea7da97 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -5,8 +5,6 @@ using DotNetOpenAuth.Messaging; internal static class Util { - internal static readonly Random NonCryptoRandomDataGenerator = new Random(); - /// <summary> /// Enumerates through the individual set bits in a flag enum. /// </summary> @@ -30,17 +28,6 @@ } /// <summary> - /// Gets a buffer of random data (not cryptographically strong). - /// </summary> - /// <param name="length">The length of the sequence to generate.</param> - /// <returns>The generated values, which may contain zeros.</returns> - internal static byte[] GetNonCryptoRandomData(int length) { - byte[] buffer = new byte[length]; - NonCryptoRandomDataGenerator.NextBytes(buffer); - return buffer; - } - - /// <summary> /// Copies the contents of one stream to another. /// </summary> /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param> 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/DataApi.cs b/samples/OAuthServiceProvider/App_Code/DataApi.cs index a765159..00876f6 100644 --- a/samples/OAuthServiceProvider/App_Code/DataApi.cs +++ b/samples/OAuthServiceProvider/App_Code/DataApi.cs @@ -1,20 +1,26 @@ using System.Linq; using System.ServiceModel; +/// <summary> +/// The WCF service API. +/// </summary> +/// <remarks> +/// Note how there is no code here that is bound to OAuth or any other +/// credential/authorization scheme. That's all part of the channel/binding elsewhere. +/// And the reference to Global.LoggedInUser is the user being impersonated by the WCF client. +/// In the OAuth case, it is the user who authorized the OAuth access token that was used +/// to gain access to the service. +/// </remarks> public class DataApi : IDataApi { - private static OAuthToken AccessToken { - get { return OperationContext.Current.IncomingMessageProperties["OAuthAccessToken"] as OAuthToken; } - } - public int? GetAge() { - return AccessToken.User.Age; + return Global.LoggedInUser.Age; } public string GetName() { - return AccessToken.User.FullName; + return Global.LoggedInUser.FullName; } public string[] GetFavoriteSites() { - return AccessToken.User.FavoriteSites.Select(site => site.SiteUrl).ToArray(); + return Global.LoggedInUser.FavoriteSites.Select(site => site.SiteUrl).ToArray(); } } 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..710508d 100644 --- a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs @@ -14,14 +14,30 @@ 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 && t.State != TokenAuthorizationState.AccessToken); + } catch (InvalidOperationException ex) { + throw new KeyNotFoundException("Unrecognized token", ex); + } + } + + public IServiceProviderAccessToken GetAccessToken(string token) { + try { + return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State == TokenAuthorizationState.AccessToken); + } catch (InvalidOperationException ex) { + throw new KeyNotFoundException("Unrecognized token", ex); + } } #endregion @@ -51,6 +67,7 @@ public class DatabaseTokenManager : IServiceProviderTokenManager { }; Global.DataContext.OAuthTokens.InsertOnSubmit(newToken); + Global.DataContext.SubmitChanges(); } /// <summary> diff --git a/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs b/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs index fce1ad4..1ec2cb5 100644 --- a/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs +++ b/samples/OAuthServiceProvider/App_Code/OAuthAuthorizationManager.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.IdentityModel.Policy; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; +using System.ServiceModel.Security; using DotNetOpenAuth; using DotNetOpenAuth.OAuth; @@ -24,10 +27,22 @@ public class OAuthAuthorizationManager : ServiceAuthorizationManager { if (auth != null) { var accessToken = Global.DataContext.OAuthTokens.Single(token => token.Token == auth.AccessToken); + var policy = new OAuthPrincipalAuthorizationPolicy(sp.CreatePrincipal(auth)); + var policies = new List<IAuthorizationPolicy> { + policy, + }; + var securityContext = new ServiceSecurityContext(policies.AsReadOnly()); + if (operationContext.IncomingMessageProperties.Security != null) { + operationContext.IncomingMessageProperties.Security.ServiceSecurityContext = securityContext; + } else { + operationContext.IncomingMessageProperties.Security = new SecurityMessageProperty { + ServiceSecurityContext = securityContext, + }; + } + // Only allow this method call if the access token scope permits it. string[] scopes = accessToken.Scope.Split('|'); if (scopes.Contains(operationContext.IncomingMessageHeaders.Action)) { - operationContext.IncomingMessageProperties["OAuthAccessToken"] = accessToken; return true; } } 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/OAuthPrincipalAuthorizationPolicy.cs b/samples/OAuthServiceProvider/App_Code/OAuthPrincipalAuthorizationPolicy.cs new file mode 100644 index 0000000..5bd6b05 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthPrincipalAuthorizationPolicy.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Claims; +using System.IdentityModel.Policy; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public class OAuthPrincipalAuthorizationPolicy : IAuthorizationPolicy { + private readonly Guid uniqueId = Guid.NewGuid(); + private readonly OAuthPrincipal principal; + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthPrincipalAuthorizationPolicy"/> class. + /// </summary> + /// <param name="principal">The principal.</param> + public OAuthPrincipalAuthorizationPolicy(OAuthPrincipal principal) { + this.principal = principal; + } + + #region IAuthorizationComponent Members + + /// <summary> + /// Gets a unique ID for this instance. + /// </summary> + public string Id { + get { return this.uniqueId.ToString(); } + } + + #endregion + + #region IAuthorizationPolicy Members + + public ClaimSet Issuer { + get { return ClaimSet.System; } + } + + public bool Evaluate(EvaluationContext evaluationContext, ref object state) { + evaluationContext.AddClaimSet(this, new DefaultClaimSet(Claim.CreateNameClaim(this.principal.Identity.Name))); + evaluationContext.Properties["Principal"] = this.principal; + return true; + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs new file mode 100644 index 0000000..fc1d6c5 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------- +// <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, IServiceProviderAccessToken { + #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 + + #region IServiceProviderAccessToken Members + + string IServiceProviderAccessToken.Token { + get { return this.Token; } + } + + DateTime? IServiceProviderAccessToken.ExpirationDate { + get { return null; } + } + + string IServiceProviderAccessToken.Username { + get { return this.User.OpenIDClaimedIdentifier; } + } + + string[] IServiceProviderAccessToken.Roles { + get { return this.Scope.Split('|'); } + } + + #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/OAuthServiceProvider/Web.config b/samples/OAuthServiceProvider/Web.config index 894ad38..d039daa 100644 --- a/samples/OAuthServiceProvider/Web.config +++ b/samples/OAuthServiceProvider/Web.config @@ -143,7 +143,9 @@ <behavior name="DataApiBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> - <serviceAuthorization serviceAuthorizationManagerType="OAuthAuthorizationManager, __code"/> + <serviceAuthorization + serviceAuthorizationManagerType="OAuthAuthorizationManager, __code" + principalPermissionMode="Custom" /> </behavior> </serviceBehaviors> </behaviors> diff --git a/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs index 2b9e01c..6dc210d 100644 --- a/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs +++ b/samples/OpenIdProviderMvc/Code/AnonymousIdentifierProvider.cs @@ -1,15 +1,29 @@ namespace OpenIdProviderMvc.Code { using System; using System.Web.Security; - using DotNetOpenAuth.ApplicationBlock.Provider; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Provider; using OpenIdProviderMvc.Models; - internal class AnonymousIdentifierProvider : AnonymousIdentifierProviderBase { + internal class AnonymousIdentifierProvider : PrivatePersonalIdentifierProviderBase { + /// <summary> + /// Initializes a new instance of the <see cref="AnonymousIdentifierProvider"/> class. + /// </summary> internal AnonymousIdentifierProvider() : base(Util.GetAppPathRootedUri("anon?id=")) { } + /// <summary> + /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier. + /// </summary> + /// <param name="localIdentifier">The OP local identifier.</param> + /// <returns>The salt to use in the hash.</returns> + /// <remarks> + /// It is important that this method always return the same value for a given + /// <paramref name="localIdentifier"/>. + /// New salts can be generated for local identifiers without previously assigned salt + /// values by calling <see cref="CreateSalt"/> or by a custom method. + /// </remarks> protected override byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier) { // This is just a sample with no database... a real web app MUST return // a reasonable salt here and have that salt be persistent for each user. @@ -17,7 +31,12 @@ string username = User.GetUserFromClaimedIdentifier(new Uri(localIdentifier)); string salt = membership.GetSalt(username); return Convert.FromBase64String(salt); - ////return AnonymousIdentifierProviderBase.GetNewSalt(5); + + // If users were encountered without a salt, one could be generated like this, + // and would also need to be saved to the user's account. + //// var newSalt = AnonymousIdentifierProviderBase.GetNewSalt(5); + //// user.Salt = newSalt; + //// return newSalt; } } } diff --git a/samples/OpenIdProviderMvc/Controllers/HomeController.cs b/samples/OpenIdProviderMvc/Controllers/HomeController.cs index 346e838..5ba08b3 100644 --- a/samples/OpenIdProviderMvc/Controllers/HomeController.cs +++ b/samples/OpenIdProviderMvc/Controllers/HomeController.cs @@ -23,9 +23,5 @@ public ActionResult Xrds() { return View(); } - - public ActionResult PpidXrds() { - return View(); - } } } diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs index e353268..bd0fdbf 100644 --- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs +++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs @@ -5,9 +5,10 @@ namespace OpenIdProviderMvc.Controllers { using System.Web; using System.Web.Mvc; using System.Web.Mvc.Ajax; - using DotNetOpenAuth.ApplicationBlock.Provider; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Behaviors; + using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; using DotNetOpenAuth.OpenId.Provider; using OpenIdProviderMvc.Code; @@ -20,67 +21,18 @@ namespace OpenIdProviderMvc.Controllers { } [ValidateInput(false)] - public ActionResult PpidProvider() { - return this.DoProvider(true); - } - - [ValidateInput(false)] public ActionResult Provider() { - return this.DoProvider(false); - } - - [Authorize] - public ActionResult SendAssertion(bool pseudonymous) { - IAuthenticationRequest authReq = PendingAuthenticationRequest; - PendingAuthenticationRequest = null; - if (authReq == null) { - throw new InvalidOperationException(); - } - - Identifier localIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); - - if (pseudonymous) { - if (!authReq.IsDirectedIdentity) { - throw new InvalidOperationException("Directed identity is the only supported scenario for anonymous identifiers."); - } - - var anonProvider = new AnonymousIdentifierProvider(); - authReq.ScrubPersonallyIdentifiableInformation(localIdentifier, anonProvider, true); - authReq.IsAuthenticated = true; - } else { - if (authReq.IsDirectedIdentity) { - authReq.LocalIdentifier = localIdentifier; - authReq.ClaimedIdentifier = localIdentifier; - authReq.IsAuthenticated = true; - } else { - if (authReq.LocalIdentifier == localIdentifier) { - authReq.IsAuthenticated = true; - if (!authReq.IsDelegatedIdentifier) { - authReq.ClaimedIdentifier = authReq.LocalIdentifier; - } - } else { - authReq.IsAuthenticated = false; - } - } - - // TODO: Respond to AX/sreg extension requests here. - // We don't want to add these extension responses for anonymous identifiers - // because they could leak information about the user's identity. - } - - return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); - } - - private ActionResult DoProvider(bool pseudonymous) { IRequest request = OpenIdProvider.GetRequest(); if (request != null) { var authRequest = request as IAuthenticationRequest; if (authRequest != null) { PendingAuthenticationRequest = authRequest; - if (User.Identity.IsAuthenticated && (authRequest.IsDirectedIdentity || Models.User.GetClaimedIdentifierForUser(User.Identity.Name) == authRequest.LocalIdentifier)) { - return this.SendAssertion(pseudonymous); + 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", new { pseudonymous = pseudonymous }) }); + return RedirectToAction("LogOn", "Account", new { returnUrl = Url.Action("SendAssertion") }); } } @@ -93,5 +45,46 @@ namespace OpenIdProviderMvc.Controllers { return View(); } } + + [Authorize] + public ActionResult SendAssertion() { + IAuthenticationRequest authReq = PendingAuthenticationRequest; + PendingAuthenticationRequest = null; // clear session static so we don't do this again + if (authReq == null) { + throw new InvalidOperationException("There's no pending authentication request!"); + } + + if (authReq.IsDirectedIdentity) { + authReq.LocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + } + if (!authReq.IsDelegatedIdentifier) { + authReq.ClaimedIdentifier = authReq.LocalIdentifier; + } + + // Respond to AX/sreg extension requests. + //// Real web sites would have code here + + authReq.IsAuthenticated = this.UserControlsIdentifier(authReq); + return OpenIdProvider.PrepareResponse(authReq).AsActionResult(); + } + + /// <summary> + /// Checks whether the logged in user controls the OP local identifier in the given authentication request. + /// </summary> + /// <param name="authReq">The authentication request.</param> + /// <returns><c>true</c> if the user controls the identifier; <c>false</c> otherwise.</returns> + private bool UserControlsIdentifier(IAuthenticationRequest authReq) { + if (authReq == null) { + throw new ArgumentNullException("authReq"); + } + + if (User == null || User.Identity == null) { + return false; + } + + Uri userLocalIdentifier = Models.User.GetClaimedIdentifierForUser(User.Identity.Name); + return authReq.LocalIdentifier == userLocalIdentifier || + authReq.LocalIdentifier == PpidGeneration.PpidIdentifierProvider.GetIdentifier(userLocalIdentifier, authReq.Realm); + } } } diff --git a/samples/OpenIdProviderMvc/Controllers/UserController.cs b/samples/OpenIdProviderMvc/Controllers/UserController.cs index c160fce..3cb87ae 100644 --- a/samples/OpenIdProviderMvc/Controllers/UserController.cs +++ b/samples/OpenIdProviderMvc/Controllers/UserController.cs @@ -9,7 +9,7 @@ namespace OpenIdProviderMvc.Controllers { public class UserController : Controller { public ActionResult PpidIdentity() { if (Request.AcceptTypes.Contains("application/xrds+xml")) { - return View("PpidXrds"); + return View("Xrds"); } return View(); @@ -33,10 +33,6 @@ namespace OpenIdProviderMvc.Controllers { return View(); } - public ActionResult PpidXrds() { - return View(); - } - private ActionResult RedirectIfNotNormalizedRequestUri() { Uri normalized = Models.User.GetNormalizedClaimedIdentifier(Request.Url); if (Request.Url != normalized) { diff --git a/samples/OpenIdProviderMvc/Global.asax.cs b/samples/OpenIdProviderMvc/Global.asax.cs index 8c57961..a2bcfb2 100644 --- a/samples/OpenIdProviderMvc/Global.asax.cs +++ b/samples/OpenIdProviderMvc/Global.asax.cs @@ -26,10 +26,6 @@ "anon", new { controller = "User", action = "PpidIdentity", id = string.Empty }); routes.MapRoute( - "PpidXrds", - "PpidXrds", - new { controller = "Home", action = "PpidXrds" }); // Parameter defaults - routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = string.Empty }); // Parameter defaults @@ -37,6 +33,7 @@ protected void Application_Start() { RegisterRoutes(RouteTable.Routes); + DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider(); } } }
\ No newline at end of file diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj index 5caf26d..f568538 100644 --- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj +++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj @@ -92,10 +92,8 @@ <Content Include="Views\Account\ChangePassword.aspx" /> <Content Include="Views\Account\ChangePasswordSuccess.aspx" /> <Content Include="Views\Account\Register.aspx" /> - <Content Include="Views\Home\PpidXrds.aspx" /> <Content Include="Views\Home\Xrds.aspx" /> <Content Include="Views\OpenId\Provider.aspx" /> - <Content Include="Views\User\PpidXrds.aspx" /> <Content Include="Views\User\PpidIdentity.aspx" /> <Content Include="Views\User\Identity.aspx" /> <Content Include="Views\User\Xrds.aspx" /> diff --git a/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx b/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx deleted file mode 100644 index 990a3df..0000000 --- a/samples/OpenIdProviderMvc/Views/Home/PpidXrds.aspx +++ /dev/null @@ -1,18 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" ContentType="application/xrds+xml" %><?xml version="1.0" encoding="UTF-8"?> -<%-- -This page is a required as part of the service discovery phase of the openid -protocol (step 1). It simply renders the xml for doing service discovery of -server.aspx using the xrds mechanism. -This XRDS doc is discovered via the user.aspx page. ---%> -<xrds:XRDS - xmlns:xrds="xri://$xrds" - xmlns:openid="http://openid.net/xmlns/1.0" - xmlns="xri://$xrd*($v*2.0)"> - <XRD> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/server</Type> - <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> - </Service> - </XRD> -</xrds:XRDS> diff --git a/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx b/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx index f33a694..655e5d6 100644 --- a/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx +++ b/samples/OpenIdProviderMvc/Views/User/PpidIdentity.aspx @@ -9,7 +9,7 @@ <op:IdentityEndpoint ID="IdentityEndpoint11" runat="server" ProviderEndpointUrl="~/OpenId/PpidProvider" ProviderVersion="V11" /> <op:IdentityEndpoint ID="IdentityEndpoint20" runat="server" ProviderEndpointUrl="~/OpenId/PpidProvider" - XrdsUrl="~/User/all/ppidxrds" XrdsAutoAnswer="false" /> + XrdsUrl="~/User/all/xrds" XrdsAutoAnswer="false" /> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>OpenID identity page </h2> diff --git a/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx b/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx deleted file mode 100644 index 67256bd..0000000 --- a/samples/OpenIdProviderMvc/Views/User/PpidXrds.aspx +++ /dev/null @@ -1,13 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="true" ContentType="application/xrds+xml" %><?xml version="1.0" encoding="UTF-8"?> -<XRDS xmlns="xri://$xrds" xmlns:openid="http://openid.net/xmlns/1.0"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> - </Service> - <Service priority="20"> - <Type>http://openid.net/signon/1.0</Type> - <URI><%=new Uri(Request.Url, Response.ApplyAppPathModifier("~/OpenId/PpidProvider"))%></URI> - </Service> - </XRD> -</XRDS> diff --git a/samples/OpenIdProviderMvc/Web.config b/samples/OpenIdProviderMvc/Web.config index 7b078ef..fb89415 100644 --- a/samples/OpenIdProviderMvc/Web.config +++ b/samples/OpenIdProviderMvc/Web.config @@ -46,11 +46,17 @@ <!-- this is an optional configuration section where aspects of dotnetopenauth can be customized --> <dotNetOpenAuth> <openid> - <relyingParty> - <security requireSsl="false"/> + <provider> + <security requireSsl="false" /> + <behaviors> + <!-- Behaviors activate themselves automatically for individual matching requests. + The first one in this list to match an incoming request "owns" the request. If no + profile matches, the default behavior is assumed. --> + <add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" /> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="RelyingPartyWebForms.CustomStore, RelyingPartyWebForms" />--> - </relyingParty> + </provider> </openid> <messaging> <untrustedWebRequest> diff --git a/samples/OpenIdProviderWebForms/Web.config b/samples/OpenIdProviderWebForms/Web.config index 7626751..c3c7ef9 100644 --- a/samples/OpenIdProviderWebForms/Web.config +++ b/samples/OpenIdProviderWebForms/Web.config @@ -47,6 +47,13 @@ <dotNetOpenAuth> <openid> <provider> + <security requireSsl="false" /> + <behaviors> + <!-- Behaviors activate themselves automatically for individual matching requests. + The first one in this list to match an incoming request "owns" the request. If no + profile matches, the default behavior is assumed. --> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" />--> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="OpenIdProviderWebForms.Code.CustomStore, OpenIdProviderWebForms" />--> </provider> diff --git a/samples/OpenIdRelyingPartyMvc/Web.config b/samples/OpenIdRelyingPartyMvc/Web.config index ba97122..c4a4b71 100644 --- a/samples/OpenIdRelyingPartyMvc/Web.config +++ b/samples/OpenIdRelyingPartyMvc/Web.config @@ -47,6 +47,11 @@ <openid> <relyingParty> <security requireSsl="false" /> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + </behaviors> </relyingParty> </openid> <messaging> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx b/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx index 46458e8..cbc13ee 100644 --- a/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/Default.aspx @@ -1,6 +1,6 @@ <%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="~/Site.Master" %> <%@ Import Namespace="OpenIdRelyingPartyWebForms" %> - +<%@ Register Src="~/MembersOnly/ProfileFieldsDisplay.ascx" TagPrefix="cc1" TagName="ProfileFieldsDisplay" %> <asp:Content ID="Content1" runat="server" ContentPlaceHolderID="Main"> <h2> Members Only Area @@ -22,85 +22,13 @@ </ul> <% } %> -<% if (State.ProfileFields != null) { %> +<% if (State.ProfileFields != null) { + profileFieldsDisplay.ProfileValues = State.ProfileFields; %> <p> In addition to authenticating you, your OpenID Provider may have told us something about you using the Simple Registration extension: </p> - <table id="profileFieldsTable" runat="server"> - <tr> - <td> - Nickname - </td> - <td> - <%=State.ProfileFields.Nickname %> - </td> - </tr> - <tr> - <td> - Email - </td> - <td> - <%=State.ProfileFields.Email%> - </td> - </tr> - <tr> - <td> - FullName - </td> - <td> - <%=State.ProfileFields.FullName%> - </td> - </tr> - <tr> - <td> - Date of Birth - </td> - <td> - <%=State.ProfileFields.BirthDate.ToString()%> - </td> - </tr> - <tr> - <td> - Gender - </td> - <td> - <%=State.ProfileFields.Gender.ToString()%> - </td> - </tr> - <tr> - <td> - Post Code - </td> - <td> - <%=State.ProfileFields.PostalCode%> - </td> - </tr> - <tr> - <td> - Country - </td> - <td> - <%=State.ProfileFields.Country%> - </td> - </tr> - <tr> - <td> - Language - </td> - <td> - <%=State.ProfileFields.Language%> - </td> - </tr> - <tr> - <td> - Timezone - </td> - <td> - <%=State.ProfileFields.TimeZone%> - </td> - </tr> - </table> + <cc1:ProfileFieldsDisplay runat="server" ID="profileFieldsDisplay" /> <% } %> </asp:Content> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx new file mode 100644 index 0000000..b2e5f7e --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx @@ -0,0 +1,75 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProfileFieldsDisplay.ascx.cs" Inherits="OpenIdRelyingPartyWebForms.MembersOnly.ProfileFieldsDisplay" %> +<table id="profileFieldsTable" runat="server"> + <tr> + <td> + Nickname + </td> + <td> + <%=ProfileValues.Nickname %> + </td> + </tr> + <tr> + <td> + Email + </td> + <td> + <%=ProfileValues.Email%> + </td> + </tr> + <tr> + <td> + FullName + </td> + <td> + <%=ProfileValues.FullName%> + </td> + </tr> + <tr> + <td> + Date of Birth + </td> + <td> + <%=ProfileValues.BirthDate.ToString()%> + </td> + </tr> + <tr> + <td> + Gender + </td> + <td> + <%=ProfileValues.Gender.ToString()%> + </td> + </tr> + <tr> + <td> + Post Code + </td> + <td> + <%=ProfileValues.PostalCode%> + </td> + </tr> + <tr> + <td> + Country + </td> + <td> + <%=ProfileValues.Country%> + </td> + </tr> + <tr> + <td> + Language + </td> + <td> + <%=ProfileValues.Language%> + </td> + </tr> + <tr> + <td> + Timezone + </td> + <td> + <%=ProfileValues.TimeZone%> + </td> + </tr> +</table> diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs new file mode 100644 index 0000000..4fb127e --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.cs @@ -0,0 +1,13 @@ +namespace OpenIdRelyingPartyWebForms.MembersOnly { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + + public partial class ProfileFieldsDisplay : System.Web.UI.UserControl { + public ClaimsResponse ProfileValues { get; set; } + } +}
\ No newline at end of file diff --git a/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs new file mode 100644 index 0000000..4a5dc8a --- /dev/null +++ b/samples/OpenIdRelyingPartyWebForms/MembersOnly/ProfileFieldsDisplay.ascx.designer.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4918 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace OpenIdRelyingPartyWebForms.MembersOnly { + + + public partial class ProfileFieldsDisplay { + + /// <summary> + /// profileFieldsTable 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.HtmlTable profileFieldsTable; + } +} diff --git a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj index 556dadf..d3bf92c 100644 --- a/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj +++ b/samples/OpenIdRelyingPartyWebForms/OpenIdRelyingPartyWebForms.csproj @@ -132,6 +132,13 @@ <Compile Include="MembersOnly\DisplayGoogleContacts.aspx.designer.cs"> <DependentUpon>DisplayGoogleContacts.aspx</DependentUpon> </Compile> + <Compile Include="MembersOnly\ProfileFieldsDisplay.ascx.cs"> + <DependentUpon>ProfileFieldsDisplay.ascx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="MembersOnly\ProfileFieldsDisplay.ascx.designer.cs"> + <DependentUpon>ProfileFieldsDisplay.ascx</DependentUpon> + </Compile> <Compile Include="m\Login.aspx.cs"> <DependentUpon>Login.aspx</DependentUpon> <SubType>ASPXCodeBehind</SubType> @@ -168,6 +175,7 @@ <Content Include="images\yahoo.png" /> <Content Include="loginPlusOAuth.aspx" /> <Content Include="MembersOnly\DisplayGoogleContacts.aspx" /> + <Content Include="MembersOnly\ProfileFieldsDisplay.ascx" /> <Content Include="MembersOnly\Web.config" /> <Content Include="m\Login.aspx" /> <Content Include="NoIdentityOpenId.aspx" /> diff --git a/samples/OpenIdRelyingPartyWebForms/Web.config b/samples/OpenIdRelyingPartyWebForms/Web.config index 2a13828..0ae3c6c 100644 --- a/samples/OpenIdRelyingPartyWebForms/Web.config +++ b/samples/OpenIdRelyingPartyWebForms/Web.config @@ -29,6 +29,11 @@ <openid> <relyingParty> <security requireSsl="false" /> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="OpenIdRelyingPartyWebForms.CustomStore, OpenIdRelyingPartyWebForms" />--> </relyingParty> 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/samples/OpenIdRelyingPartyWebForms/login.aspx b/samples/OpenIdRelyingPartyWebForms/login.aspx index 20294e7..281725c 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx @@ -5,7 +5,7 @@ <asp:Content runat="server" ContentPlaceHolderID="Main"> <h2>Login Page </h2> <rp:OpenIdLogin ID="OpenIdLogin1" runat="server" CssClass="openid_login" RequestCountry="Request" - RequestEmail="Request" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require" + RequestEmail="Require" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require" RememberMeVisible="True" PolicyUrl="~/PrivacyPolicy.aspx" TabIndex="1" OnLoggedIn="OpenIdLogin1_LoggedIn" OnLoggingIn="OpenIdLogin1_LoggingIn" OnSetupRequired="OpenIdLogin1_SetupRequired" /> @@ -13,17 +13,20 @@ <asp:CheckBox ID="requireSslCheckBox" runat="server" Text="RequireSsl (high security) mode" oncheckedchanged="requireSslCheckBox_CheckedChanged" /><br /> + <h4 style="margin-top: 0; margin-bottom: 0">PAPE policies</h4> <asp:CheckBoxList runat="server" ID="papePolicies"> <asp:ListItem Text="Request phishing resistant authentication" Value="http://schemas.openid.net/pape/policies/2007/06/phishing-resistant" /> <asp:ListItem Text="Request multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor" /> <asp:ListItem Text="Request physical multi-factor authentication" Value="http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical" /> + <asp:ListItem Text="Request PPID identifier" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" /> </asp:CheckBoxList> + <p>Try the PPID identifier functionality against the OpenIDProviderMvc sample.</p> </fieldset> <br /> <asp:Label ID="setupRequiredLabel" runat="server" EnableViewState="False" Text="You must log into your Provider first to use Immediate mode." Visible="False" /> <p> - <asp:ImageButton runat="server" ImageUrl="~/images/yahoo.png" ID="yahooLoginButton" - OnClick="yahooLoginButton_Click" /> + <rp:OpenIdButton runat="server" ImageUrl="~/images/yahoo.png" Text="Login with Yahoo!" ID="yahooLoginButton" + Identifier="https://me.yahoo.com/" /> </p> </asp:Content> diff --git a/samples/OpenIdRelyingPartyWebForms/login.aspx.cs b/samples/OpenIdRelyingPartyWebForms/login.aspx.cs index fb961dd..1de942a 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx.cs +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx.cs @@ -39,16 +39,6 @@ namespace OpenIdRelyingPartyWebForms { this.setupRequiredLabel.Visible = true; } - protected void yahooLoginButton_Click(object sender, ImageClickEventArgs e) { - OpenIdRelyingParty openid = new OpenIdRelyingParty(); - var req = openid.CreateRequest("yahoo.com"); - this.prepareRequest(req); - req.RedirectToProvider(); - - // We don't listen for the response from the provider explicitly - // because the OpenIdLogin control is already doing that for us. - } - private void prepareRequest(IAuthenticationRequest request) { // Collect the PAPE policies requested by the user. List<string> policies = new List<string>(); diff --git a/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs b/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs index 60de3ff..944f5ff 100644 --- a/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs +++ b/samples/OpenIdRelyingPartyWebForms/login.aspx.designer.cs @@ -56,6 +56,6 @@ namespace OpenIdRelyingPartyWebForms { /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// </remarks> - protected global::System.Web.UI.WebControls.ImageButton yahooLoginButton; + protected global::DotNetOpenAuth.OpenId.RelyingParty.OpenIdButton yahooLoginButton; } } |