diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 08:00:50 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 08:00:50 -0700 |
commit | ad60330d66985c4892b7e0b7ddb424be9ca867c8 (patch) | |
tree | 5efdb30c9b14e1928828b76c8a66822779a17198 | |
parent | 52a77983f11cfbb948a574585ba8069dcbcbd89b (diff) | |
download | DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.zip DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.tar.gz DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.tar.bz2 |
More work toward a working authorization server.
29 files changed, 603 insertions, 247 deletions
diff --git a/samples/OAuthConsumer/OAuthConsumer.csproj b/samples/OAuthConsumer/OAuthConsumer.csproj index 663b4cf..fa5acff 100644 --- a/samples/OAuthConsumer/OAuthConsumer.csproj +++ b/samples/OAuthConsumer/OAuthConsumer.csproj @@ -70,6 +70,7 @@ <Generator>WCF Proxy Generator</Generator> <LastGenOutput>Reference.cs</LastGenOutput> </None> + <Content Include="SampleWcf2.aspx" /> <Content Include="SignInWithTwitter.aspx" /> <Content Include="TracePage.aspx" /> <Content Include="Twitter.aspx" /> @@ -105,6 +106,13 @@ <Compile Include="SampleWcf.aspx.designer.cs"> <DependentUpon>SampleWcf.aspx</DependentUpon> </Compile> + <Compile Include="SampleWcf2.aspx.cs"> + <DependentUpon>SampleWcf2.aspx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="SampleWcf2.aspx.designer.cs"> + <DependentUpon>SampleWcf2.aspx</DependentUpon> + </Compile> <Compile Include="Service References\SampleServiceProvider\Reference.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> diff --git a/samples/OAuthConsumer/SampleWcf2.aspx b/samples/OAuthConsumer/SampleWcf2.aspx new file mode 100644 index 0000000..e8d672b --- /dev/null +++ b/samples/OAuthConsumer/SampleWcf2.aspx @@ -0,0 +1,13 @@ +<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" Inherits="OAuthConsumer.SampleWcf2" Codebehind="SampleWcf2.aspx.cs" %> + +<asp:Content ID="Content2" ContentPlaceHolderID="Body" runat="Server"> + <fieldset title="Authorization"> + <asp:CheckBoxList runat="server" ID="scopeList"> + <asp:ListItem Value="http://tempuri.org/IDataApi/GetName">GetName</asp:ListItem> + <asp:ListItem Value="http://tempuri.org/IDataApi/GetAge">GetAge</asp:ListItem> + <asp:ListItem Value="http://tempuri.org/IDataApi/GetFavoriteSites">GetFavoriteSites</asp:ListItem> + </asp:CheckBoxList> + <asp:Button ID="getAuthorizationButton" runat="server" Text="Get Authorization" OnClick="getAuthorizationButton_Click" /> + <asp:Label ID="authorizationLabel" runat="server" /> + </fieldset> +</asp:Content>
\ No newline at end of file diff --git a/samples/OAuthConsumer/SampleWcf2.aspx.cs b/samples/OAuthConsumer/SampleWcf2.aspx.cs new file mode 100644 index 0000000..2361d36 --- /dev/null +++ b/samples/OAuthConsumer/SampleWcf2.aspx.cs @@ -0,0 +1,48 @@ +using DotNetOpenAuth.OAuthWrap; + +namespace OAuthConsumer { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + + public partial class SampleWcf2 : System.Web.UI.Page { + protected void Page_Load(object sender, EventArgs e) { + if (!IsPostBack) + { + var client = CreateClient(); + var authorization = client.ProcessUserAuthorization(); + if (authorization != null) + { + Response.Write("Obtained access token: " + authorization.AccessToken); + } + } + } + + protected void getAuthorizationButton_Click(object sender, EventArgs e) { + var client = CreateClient(); + var response = client.PrepareRequestUserAuthorization(); + + string[] scopes = (from item in this.scopeList.Items.OfType<ListItem>() + where item.Selected + select item.Value).ToArray(); + response.Scope = string.Join("|", scopes); + client.Channel.Send(response); + } + + private static WebAppClient CreateClient() { + var authServerDescription = new AuthorizationServerDescription { + TokenEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/token"), + AuthorizationEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/auth"), + }; + var client = new WebAppClient(authServerDescription) + { + ClientIdentifier = "sampleconsumer", + ClientSecret = "samplesecret", + }; + return client; + } + } +}
\ No newline at end of file diff --git a/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs b/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs new file mode 100644 index 0000000..3046936 --- /dev/null +++ b/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace OAuthConsumer { + + + public partial class SampleWcf2 { + + /// <summary> + /// scopeList 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.CheckBoxList scopeList; + + /// <summary> + /// getAuthorizationButton 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.Button getAuthorizationButton; + + /// <summary> + /// authorizationLabel 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.Label authorizationLabel; + } +} diff --git a/samples/OAuthConsumerWpf/MainWindow.xaml b/samples/OAuthConsumerWpf/MainWindow.xaml index 32cd758..69b23f6 100644 --- a/samples/OAuthConsumerWpf/MainWindow.xaml +++ b/samples/OAuthConsumerWpf/MainWindow.xaml @@ -2,138 +2,138 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="DotNetOpenAuth Consumer (sample)" Height="400" Width="442"> - <TabControl Name="outerTabControl" Margin="0,10,0,0"> - <TabItem Header="Google" Name="googleTab"> - <Grid Margin="5"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition /> - </Grid.RowDefinitions> - <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"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="Auto" /> - </Grid.ColumnDefinitions> - </Grid> - </TabItem> - <TabItem Header="Blogger" Name="bloggerTab"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="*" /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - </Grid.RowDefinitions> - <Label>Blog URL</Label> - <TextBox Grid.Column="1" x:Name="blogUrlBox"/> - <Label Grid.Row="1">Title</Label> - <TextBox Grid.Row="1" Grid.Column="1" x:Name="postTitleBox">OAuth Rocks!</TextBox> - <Label Grid.Row="2">Body</Label> - <TextBox Grid.Row="2" Grid.Column="1" x:Name="postBodyBox" AcceptsReturn="True" AcceptsTab="True" AutoWordSelection="True" TextWrapping="WrapWithOverflow"><p xmlns="http://www.w3.org/1999/xhtml">Oauth is cool</p></TextBox> - <Button x:Name="postButton" Grid.Row="3" Grid.Column="1" Click="postButton_Click" IsEnabled="False">Post</Button> - </Grid> - </TabItem> - </TabControl> - </Grid> - </TabItem> - <TabItem Header="WCF sample"> - <Grid Margin="5"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> - <RowDefinition /> - </Grid.RowDefinitions> - <Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Name="beginWcfAuthorizationButton" Click="beginWcfAuthorizationButton_Click">Authorize</Button> - <Label Content="Name" Grid.Row="1" /> - <Label Grid.Row="1" Grid.Column="1" Name="wcfName" /> - <Label Content="Age" Grid.Row="2" /> - <Label Grid.Row="2" Grid.Column="1" Name="wcfAge" /> - <Label Content="Favorite sites" Grid.Row="3" /> - <Label Grid.Row="3" Grid.Column="1" Name="wcfFavoriteSites" /> - </Grid> - </TabItem> - <TabItem Header="Generic"> - <Grid> - <Grid.RowDefinitions> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="auto" /> - <RowDefinition Height="*" /> - </Grid.RowDefinitions> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="auto" /> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="auto" /> - </Grid.ColumnDefinitions> - <Label Grid.Row="0">Request Token URL</Label> - <TextBox Grid.Column="1" x:Name="requestTokenUrlBox" /> - <ComboBox Grid.Column="2" x:Name="requestTokenHttpMethod" SelectedIndex="1"> - <ComboBox.Items> - <ComboBoxItem>GET</ComboBoxItem> - <ComboBoxItem>POST</ComboBoxItem> - </ComboBox.Items> - </ComboBox> - <Label Grid.Row="1">Authorize URL</Label> - <TextBox Grid.Row="1" Grid.Column="1" x:Name="authorizeUrlBox" /> - <Label Grid.Row="1" Grid.Column="2">GET</Label> - <Label Grid.Row="2">Access Token URL</Label> - <TextBox Grid.Row="2" Grid.Column="1" x:Name="accessTokenUrlBox" /> - <ComboBox Grid.Row="2" Grid.Column="2" x:Name="accessTokenHttpMethod" SelectedIndex="1"> - <ComboBox.Items> - <ComboBoxItem>GET</ComboBoxItem> - <ComboBoxItem>POST</ComboBoxItem> - </ComboBox.Items> - </ComboBox> - <Label Grid.Row="3">Resource URL</Label> - <TextBox Grid.Row="3" Grid.Column="1" x:Name="resourceUrlBox" /> - <ComboBox Grid.Row="3" Grid.Column="2" x:Name="resourceHttpMethodList" SelectedIndex="0"> - <ComboBox.Items> - <ComboBoxItem>GET w/ header</ComboBoxItem> - <ComboBoxItem>GET w/ querystring</ComboBoxItem> - <ComboBoxItem>POST</ComboBoxItem> - </ComboBox.Items> - </ComboBox> - <Label Grid.Row="4">Consumer key</Label> - <TextBox Grid.Row="4" Grid.Column="1" x:Name="consumerKeyBox" Grid.ColumnSpan="2"/> - <Label Grid.Row="5">Consumer secret</Label> - <TextBox Grid.Row="5" Grid.Column="1" x:Name="consumerSecretBox" Grid.ColumnSpan="2"/> - <Label Grid.Row="6">OAuth version</Label> - <ComboBox Grid.Row="6" Grid.Column="1" SelectedIndex="1" x:Name="oauthVersion"> - <ComboBox.Items> - <ComboBoxItem>1.0</ComboBoxItem> - <ComboBoxItem>1.0a</ComboBoxItem> - </ComboBox.Items> - </ComboBox> - <Button Grid.Row="7" Grid.Column="1" x:Name="beginButton" Click="beginButton_Click">Begin</Button> - <TextBox Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="3" Name="resultsBox" IsReadOnly="True" /> - </Grid> - </TabItem> - <TabItem Header="Generic WRAP"> + <TabControl Name="outerTabControl" Margin="0,10,0,0"> + <TabItem Header="Google" Name="googleTab"> + <Grid Margin="5"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition /> + </Grid.RowDefinitions> + <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"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + </Grid> + </TabItem> + <TabItem Header="Blogger" Name="bloggerTab"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Label>Blog URL</Label> + <TextBox Grid.Column="1" x:Name="blogUrlBox"/> + <Label Grid.Row="1">Title</Label> + <TextBox Grid.Row="1" Grid.Column="1" x:Name="postTitleBox">OAuth Rocks!</TextBox> + <Label Grid.Row="2">Body</Label> + <TextBox Grid.Row="2" Grid.Column="1" x:Name="postBodyBox" AcceptsReturn="True" AcceptsTab="True" AutoWordSelection="True" TextWrapping="WrapWithOverflow"><p xmlns="http://www.w3.org/1999/xhtml">Oauth is cool</p></TextBox> + <Button x:Name="postButton" Grid.Row="3" Grid.Column="1" Click="postButton_Click" IsEnabled="False">Post</Button> + </Grid> + </TabItem> + </TabControl> + </Grid> + </TabItem> + <TabItem Header="WCF sample"> + <Grid Margin="5"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition /> + </Grid.RowDefinitions> + <Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Name="beginWcfAuthorizationButton" Click="beginWcfAuthorizationButton_Click">Authorize</Button> + <Label Content="Name" Grid.Row="1" /> + <Label Grid.Row="1" Grid.Column="1" Name="wcfName" /> + <Label Content="Age" Grid.Row="2" /> + <Label Grid.Row="2" Grid.Column="1" Name="wcfAge" /> + <Label Content="Favorite sites" Grid.Row="3" /> + <Label Grid.Row="3" Grid.Column="1" Name="wcfFavoriteSites" /> + </Grid> + </TabItem> + <TabItem Header="Generic 1.0(a)"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="auto" /> + <RowDefinition Height="*" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="auto" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="auto" /> + </Grid.ColumnDefinitions> + <Label Grid.Row="0">Request Token URL</Label> + <TextBox Grid.Column="1" x:Name="requestTokenUrlBox" /> + <ComboBox Grid.Column="2" x:Name="requestTokenHttpMethod" SelectedIndex="1"> + <ComboBox.Items> + <ComboBoxItem>GET</ComboBoxItem> + <ComboBoxItem>POST</ComboBoxItem> + </ComboBox.Items> + </ComboBox> + <Label Grid.Row="1">Authorize URL</Label> + <TextBox Grid.Row="1" Grid.Column="1" x:Name="authorizeUrlBox" /> + <Label Grid.Row="1" Grid.Column="2">GET</Label> + <Label Grid.Row="2">Access Token URL</Label> + <TextBox Grid.Row="2" Grid.Column="1" x:Name="accessTokenUrlBox" /> + <ComboBox Grid.Row="2" Grid.Column="2" x:Name="accessTokenHttpMethod" SelectedIndex="1"> + <ComboBox.Items> + <ComboBoxItem>GET</ComboBoxItem> + <ComboBoxItem>POST</ComboBoxItem> + </ComboBox.Items> + </ComboBox> + <Label Grid.Row="3">Resource URL</Label> + <TextBox Grid.Row="3" Grid.Column="1" x:Name="resourceUrlBox" /> + <ComboBox Grid.Row="3" Grid.Column="2" x:Name="resourceHttpMethodList" SelectedIndex="0"> + <ComboBox.Items> + <ComboBoxItem>GET w/ header</ComboBoxItem> + <ComboBoxItem>GET w/ querystring</ComboBoxItem> + <ComboBoxItem>POST</ComboBoxItem> + </ComboBox.Items> + </ComboBox> + <Label Grid.Row="4">Consumer key</Label> + <TextBox Grid.Row="4" Grid.Column="1" x:Name="consumerKeyBox" Grid.ColumnSpan="2"/> + <Label Grid.Row="5">Consumer secret</Label> + <TextBox Grid.Row="5" Grid.Column="1" x:Name="consumerSecretBox" Grid.ColumnSpan="2"/> + <Label Grid.Row="6">OAuth version</Label> + <ComboBox Grid.Row="6" Grid.Column="1" SelectedIndex="1" x:Name="oauthVersion"> + <ComboBox.Items> + <ComboBoxItem>1.0</ComboBoxItem> + <ComboBoxItem>1.0a</ComboBoxItem> + </ComboBox.Items> + </ComboBox> + <Button Grid.Row="7" Grid.Column="1" x:Name="beginButton" Click="beginButton_Click">Begin</Button> + <TextBox Grid.Column="0" Grid.Row="8" Grid.ColumnSpan="3" Name="resultsBox" IsReadOnly="True" /> + </Grid> + </TabItem> + <TabItem Header="Generic 2.0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto" /> @@ -151,15 +151,23 @@ <ColumnDefinition Width="*" /> <ColumnDefinition Width="auto" /> </Grid.ColumnDefinitions> - <Label Grid.Row="0">Access Token URL</Label> - <TextBox Grid.Column="1" x:Name="wrapAccessTokenUrlBox" /> + <Label Grid.Row="0">Token Endpoint URL</Label> + <TextBox Grid.Column="1" x:Name="wrapTokenUrlBox" /> <Label Grid.Column="2">POST</Label> - <Label Grid.Row="1">Refresh Token URL</Label> - <TextBox Grid.Row="1" Grid.Column="1" x:Name="wrapRefreshTokenUrlBox" /> - <Label Grid.Row="1" Grid.Column="2">POST</Label> - <Label Grid.Row="2">User Authorization URL</Label> - <TextBox Grid.Row="2" Grid.Column="1" x:Name="wrapUserAuthorizationUrlBox" /> - <Label Grid.Row="2" Grid.Column="2">GET</Label> + <Label Grid.Row="1">User Authorization URL</Label> + <TextBox Grid.Row="1" Grid.Column="1" x:Name="wrapAuthorizationUrlBox" /> + <Label Grid.Row="1" Grid.Column="2">GET</Label> + <Label Grid.Row="2">Flow</Label> + <ComboBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" x:Name="flowBox" SelectedIndex="0"> + <ComboBox.Items> + <ComboBoxItem>Web Server</ComboBoxItem> + <ComboBoxItem>Device</ComboBoxItem> + <ComboBoxItem>User Agent</ComboBoxItem> + <ComboBoxItem>Username and Password</ComboBoxItem> + <ComboBoxItem>Assertion</ComboBoxItem> + <ComboBoxItem>Client Credentials</ComboBoxItem> + </ComboBox.Items> + </ComboBox> <Label Grid.Row="3">Resource URL</Label> <TextBox Grid.Row="3" Grid.Column="1" x:Name="wrapResourceUrlBox" /> <ComboBox Grid.Row="3" Grid.Column="2" x:Name="wrapResourceHttpMethodList" SelectedIndex="0"> @@ -173,10 +181,10 @@ <TextBox Grid.Row="4" Grid.Column="1" x:Name="wrapClientIdentifierBox" Grid.ColumnSpan="2"/> <Label Grid.Row="5">Client Secret</Label> <TextBox Grid.Row="5" Grid.Column="1" x:Name="wrapClientSecretBox" Grid.ColumnSpan="2"/> - <Label Grid.Row="6">WRAP version</Label> + <Label Grid.Row="6">OAuth 2.0 version</Label> <ComboBox Grid.Row="6" Grid.Column="1" SelectedIndex="0" x:Name="wrapVersion"> <ComboBox.Items> - <ComboBoxItem>1.0 early DRAFT</ComboBoxItem> + <ComboBoxItem>2.0 DRAFT 5</ComboBoxItem> </ComboBox.Items> </ComboBox> <Button Grid.Row="7" Grid.Column="1" x:Name="wrapBeginButton" Click="wrapBeginButton_Click">Begin</Button> diff --git a/samples/OAuthConsumerWpf/MainWindow.xaml.cs b/samples/OAuthConsumerWpf/MainWindow.xaml.cs index 24d5249..92c9a6b 100644 --- a/samples/OAuthConsumerWpf/MainWindow.xaml.cs +++ b/samples/OAuthConsumerWpf/MainWindow.xaml.cs @@ -200,6 +200,12 @@ } private void wrapBeginButton_Click(object sender, RoutedEventArgs e) { + var authServer = new DotNetOpenAuth.OAuthWrap.AuthorizationServerDescription { + TokenEndpoint = new Uri(wrapTokenUrlBox.Text), + AuthorizationEndpoint = new Uri(wrapAuthorizationUrlBox.Text), + }; + //var client = new DotNetOpenAuth.OAuthWrap.WebAppClient(authServer); + //client.PrepareRequestUserAuthorization(); } } } diff --git a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs index be6f885..15d791e 100644 --- a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs +++ b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs @@ -1,4 +1,5 @@ -using DotNetOpenAuth.OAuth.ChannelElements; +using DotNetOpenAuth.Messaging.Bindings; +using DotNetOpenAuth.OAuth.ChannelElements; namespace OAuthServiceProvider.Code { using System; @@ -8,47 +9,28 @@ namespace OAuthServiceProvider.Code { using DotNetOpenAuth.OAuthWrap; internal class OAuth2AuthorizationServer : IAuthorizationServer { + private static readonly byte[] secret = new byte[] { 0x33, 0x55 }; // TODO: make this cryptographically strong and unique per app. + private readonly INonceStore nonceStore = new DatabaseNonceStore(); #region Implementation of IAuthorizationServer - public IConsumerDescription GetClient(string clientIdentifier) - { - throw new NotImplementedException(); - } - - #endregion - public byte[] Secret { - get { throw new NotImplementedException(); } + get { return secret; } } public DotNetOpenAuth.Messaging.Bindings.INonceStore VerificationCodeNonceStore { - get { throw new NotImplementedException(); } + get { return this.nonceStore; } } - private class ConsumerDescription : IConsumerDescription { - public string Key { - get { throw new NotImplementedException(); } - } - - public string Secret { - get { throw new NotImplementedException(); } - } - - public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { - get { throw new NotImplementedException(); } + public IConsumerDescription GetClient(string clientIdentifier) { + var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault( + consumerCandidate => consumerCandidate.ConsumerKey == clientIdentifier); + if (consumerRow == null) { + throw new ArgumentOutOfRangeException("clientIdentifier"); } - public Uri Callback { - get { throw new NotImplementedException(); } - } - - public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat { - get { throw new NotImplementedException(); } - } - - public int VerificationCodeLength { - get { throw new NotImplementedException(); } - } + return consumerRow; } + + #endregion } }
\ No newline at end of file diff --git a/samples/OAuthServiceProvider/OAuth2.ashx.cs b/samples/OAuthServiceProvider/OAuth2.ashx.cs index 17586be..cd76254 100644 --- a/samples/OAuthServiceProvider/OAuth2.ashx.cs +++ b/samples/OAuthServiceProvider/OAuth2.ashx.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Web; + using System.Web.SessionState; using Code; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuthWrap; @@ -11,29 +12,34 @@ /// <summary> /// Summary description for OAuth2 /// </summary> - public class OAuth2 : IHttpHandler { + public class OAuth2 : IHttpHandler, IRequiresSessionState { /// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> public void ProcessRequest(HttpContext context) { IDirectResponseProtocolMessage response; - if (Global.AuthorizationServer.TryPrepareAccessTokenResponse(out response)) { - Global.AuthorizationServer.Channel.Send(response); - } else { - var request = Global.AuthorizationServer.ReadAuthorizationRequest(); - if (request == null) { - throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request."); - } + switch (context.Request.PathInfo) { + case "/token": + if (Global.AuthorizationServer.TryPrepareAccessTokenResponse(out response)) { + Global.AuthorizationServer.Channel.Send(response); + } + break; + case "/auth": + var request = Global.AuthorizationServer.ReadAuthorizationRequest(); + if (request == null) { + throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request."); + } - // This sample doesn't implement support for immediate mode. - if (!request.IsUserInteractionAllowed) { - Global.AuthorizationServer.RejectAuthorizationRequest(request); - } + // This sample doesn't implement support for immediate mode. + if (!request.IsUserInteractionAllowed) { + Global.AuthorizationServer.RejectAuthorizationRequest(request); + } - // Redirect the user to a page that requires the user to be logged in. - Global.PendingOAuth2Authorization = request; - context.Response.Redirect("~/Members/Authorize2.aspx"); + // Redirect the user to a page that requires the user to be logged in. + Global.PendingOAuth2Authorization = request; + context.Response.Redirect("~/Members/Authorize2.aspx"); + break; } } diff --git a/samples/OAuthServiceProvider/Web.config b/samples/OAuthServiceProvider/Web.config index dc440fd..9a53b4a 100644 --- a/samples/OAuthServiceProvider/Web.config +++ b/samples/OAuthServiceProvider/Web.config @@ -43,7 +43,7 @@ <appSettings/> <connectionStrings> - <add name="DatabaseConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database.mdf;Integrated Security=True;User Instance=True" + <add name="DatabaseConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database3.mdf;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient" /> </connectionStrings> diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index d282777..bdfb911 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -308,12 +308,14 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Messaging\StandardMessageFactory.cs" /> <Compile Include="OAuthWrap\AuthorizationServerBase.cs" /> <Compile Include="OAuthWrap\AuthorizationState.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AuthServerBindingElementBase.cs" /> <Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.cs" /> <Compile Include="OAuthWrap\ChannelElements\OAuthWrapResourceServerChannel.cs" /> <Compile Include="Messaging\StandardMessageFactoryChannel.cs" /> <Compile Include="OAuthWrap\ChannelElements\TimestampEncoder.cs" /> <Compile Include="OAuthWrap\ChannelElements\VerificationCode.cs" /> <Compile Include="OAuthWrap\ChannelElements\WebAppVerificationCodeBindingElement.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AuthServerWebServerFlowBindingElement.cs" /> <Compile Include="OAuthWrap\IAccessTokenAnalyzer.cs" /> <Compile Include="OAuthWrap\IAuthorizationServer.cs" /> <Compile Include="OAuthWrap\IAuthorizationState.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs index 1be62f5..21fd53a 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30426.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -349,6 +349,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to The following message parts had constant value requirements that were unsatisfied: {0}. + /// </summary> + internal static string RequiredMessagePartConstantIncorrect { + get { + return ResourceManager.GetString("RequiredMessagePartConstantIncorrect", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The following required non-empty parameters were empty in the {0} message: {1}. /// </summary> internal static string RequiredNonEmptyParameterWasEmpty { diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx index cb80442..3a265f1 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx @@ -306,4 +306,7 @@ <data name="StandardMessageFactoryUnsupportedMessageType" xml:space="preserve"> <value>This message factory does not support message type(s): {0}</value> </data> -</root> + <data name="RequiredMessagePartConstantIncorrect" xml:space="preserve"> + <value>The following message parts had constant value requirements that were unsatisfied: {0}</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs index 17b5304..808d5b8 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs @@ -93,6 +93,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { try { this.CheckRequiredMessagePartsArePresent(parts.Keys, true); this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, true); + this.CheckMessagePartsConstantValues(parts, true); } catch (ProtocolException) { Logger.Messaging.ErrorFormat( "Error while performing basic validation of {0} with these message parts:{1}{2}", @@ -112,7 +113,8 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Requires<ArgumentNullException>(parts != null); return this.CheckRequiredMessagePartsArePresent(parts.Keys, false) && - this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, false); + this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, false) && + this.CheckMessagePartsConstantValues(parts, false); } /// <summary> @@ -185,6 +187,33 @@ namespace DotNetOpenAuth.Messaging.Reflection { return true; } + private bool CheckMessagePartsConstantValues(IDictionary<string, string> partValues, bool throwOnFailure) + { + Contract.Requires<ArgumentNullException>(partValues != null); + + var badConstantValues = (from part in this.Mapping.Values + where part.IsConstantValueAvailableStatically + where partValues.ContainsKey(part.Name) + where !string.Equals(partValues[part.Name], part.StaticConstantValue, StringComparison.Ordinal) + select part.Name).ToArray(); + if (badConstantValues.Length > 0) { + if (throwOnFailure) { + ErrorUtilities.ThrowProtocol( + MessagingStrings.RequiredMessagePartConstantIncorrect, + this.MessageType.FullName, + string.Join(", ", badConstantValues)); + } else { + Logger.Messaging.DebugFormat( + MessagingStrings.RequiredMessagePartConstantIncorrect, + this.MessageType.FullName, + badConstantValues.ToStringDeferred()); + return false; + } + } + + return true; + } + /// <summary> /// Reflects over some <see cref="IMessage"/>-implementing type /// and prepares to serialize/deserialize instances of that type. diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 4590c44..a9ec171 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -159,6 +159,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { (this.field.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly || (this.field.Attributes & constAttributes) == constAttributes)) { this.IsConstantValue = true; + this.IsConstantValueAvailableStatically = this.field.IsStatic; } else if (this.property != null && !this.property.CanWrite) { this.IsConstantValue = true; } @@ -194,6 +195,21 @@ namespace DotNetOpenAuth.Messaging.Reflection { internal bool IsConstantValue { get; set; } /// <summary> + /// Gets or sets a value indicating whether this part is defined as a constant field and can be read without a message instance. + /// </summary> + internal bool IsConstantValueAvailableStatically { get; set; } + + /// <summary> + /// Gets the static constant value for this message part without a message instance. + /// </summary> + internal string StaticConstantValue { + get { + Contract.Requires<InvalidOperationException>(this.IsConstantValueAvailableStatically); + return this.ToString(this.field.GetValue(null)); + } + } + + /// <summary> /// Sets the member of a given message to some given value. /// Used in deserialization. /// </summary> diff --git a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs index abf3359..2eacf93 100644 --- a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs +++ b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs @@ -144,7 +144,8 @@ namespace DotNetOpenAuth.Messaging { var matches = this.requestMessageTypes.Keys .Where(message => message.CheckMessagePartsPassBasicValidation(fields)) - .OrderByDescending(message => message.Mapping.Count) + .OrderByDescending(message => CountInCommon(message.Mapping.Keys, fields.Keys)) + .ThenByDescending(message => message.Mapping.Count) .CacheGeneratedResults(); var match = matches.FirstOrDefault(); if (match != null) { diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs index 1875bd1..f6f2041 100644 --- a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs @@ -18,6 +18,7 @@ namespace DotNetOpenAuth.OAuthWrap { protected AuthorizationServerBase(IAuthorizationServer authorizationServer) { Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); this.AuthorizationServer = authorizationServer; + this.Channel = new OAuthWrapAuthorizationServerChannel(authorizationServer); } public Channel Channel { get; set; } @@ -27,16 +28,5 @@ namespace DotNetOpenAuth.OAuthWrap { } public IAuthorizationServer AuthorizationServer { get; set; } - - protected IConsumerDescription GetClient(string clientIdentifier) { - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); - Contract.Ensures(Contract.Result<IConsumerDescription>() != null); - - try { - return this.AuthorizationServer.GetClient(clientIdentifier); - } catch (KeyNotFoundException ex) { - throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); - } - } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs new file mode 100644 index 0000000..c84d37d --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthServerBindingElementBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using Messaging; + + internal abstract class AuthServerBindingElementBase : IChannelBindingElement { + /// <summary> + /// Initializes a new instance of the <see cref="AuthServerBindingElementBase"/> class. + /// </summary> + protected AuthServerBindingElementBase() + { + } + + /// <summary> + /// Gets or sets the channel that this binding element belongs to. + /// </summary> + /// <remarks> + /// This property is set by the channel when it is first constructed. + /// </remarks> + public Channel Channel { get; set; } + + protected OAuthWrapAuthorizationServerChannel OAuthChannel { + get { return (OAuthWrapAuthorizationServerChannel)this.Channel; } + } + + /// <summary> + /// Gets the authorization server hosting this channel. + /// </summary> + /// <value>The authorization server.</value> + protected IAuthorizationServer AuthorizationServer { + get { return this.OAuthChannel.AuthorizationServer; } + } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <value></value> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public abstract MessageProtections Protection { get; } + + /// <summary> + /// Prepares a message for sending based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The message to prepare for sending.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public abstract MessageProtections? ProcessOutgoingMessage(IProtocolMessage message); + + /// <summary> + /// Performs any transformation on an incoming message that may be necessary and/or + /// validates an incoming message based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The incoming message to process.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <exception cref="ProtocolException"> + /// Thrown when the binding element rules indicate that this message is invalid and should + /// NOT be processed. + /// </exception> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public abstract MessageProtections? ProcessIncomingMessage(IProtocolMessage message); + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs new file mode 100644 index 0000000..2f7b49e --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// <copyright file="WebServerFlowBindingElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuthWrap.Messages; + using Messaging; + + internal class AuthServerWebServerFlowBindingElement : AuthServerBindingElementBase { + /// <summary> + /// Initializes a new instance of the <see cref="AuthServerWebServerFlowBindingElement"/> class. + /// </summary> + internal AuthServerWebServerFlowBindingElement() { + } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public override MessageProtections Protection { + get { return MessageProtections.None; } + } + + /// <summary> + /// Prepares a message for sending based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The message to prepare for sending.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + return null; + } + + /// <summary> + /// Performs any transformation on an incoming message that may be necessary and/or + /// validates an incoming message based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The incoming message to process.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <exception cref="ProtocolException"> + /// Thrown when the binding element rules indicate that this message is invalid and should + /// NOT be processed. + /// </exception> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + var authorizationRequest = message as WebAppRequest; + if (authorizationRequest != null) { + var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); + ErrorUtilities.VerifyProtocol(client.Callback == null || client.Callback == authorizationRequest.Callback, OAuthWrapStrings.CallbackMismatch, client.Callback, authorizationRequest.Callback); + } + return null; + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs index aae511d..f286e4d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs @@ -161,6 +161,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var bindingElements = new List<IChannelBindingElement>(); if (authorizationServer != null) { + bindingElements.Add(new AuthServerWebServerFlowBindingElement()); bindingElements.Add(new WebAppVerificationCodeBindingElement()); } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs index 3a46517..e99a685 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs @@ -20,13 +20,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// </summary> /// <param name="channel">The channel.</param> /// <param name="callback">The callback.</param> - internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, Uri callback, string scope) + internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, Uri callback, string scope, string username) : this(channel) { Contract.Requires<ArgumentNullException>(channel != null, "channel"); Contract.Requires<ArgumentNullException>(callback != null, "callback"); this.CallbackHash = this.CalculateCallbackHash(callback); this.Scope = scope; + this.User = username; this.CreationDateUtc = DateTime.UtcNow; this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); } @@ -50,10 +51,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { [MessagePart("cb")] private string CallbackHash { get; set; } - [MessagePart("scope")] + [MessagePart] internal string Scope { get; set; } - [MessagePart("nonce")] + [MessagePart] + internal string User { get; set; } + + [MessagePart] internal string Nonce { get; set; } [MessagePart("timestamp", Encoder = typeof(TimestampEncoder))] @@ -67,6 +71,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// before it passes through the channel binding elements. /// </summary> void IMessageWithEvents.OnSending() { + // Encrypt the authorizing username so as to not expose unintended private user data + // to the client or any eavesdropping third party. + if (this.User != null) { + // TODO: code here + } + this.Signature = CalculateSignature(); } @@ -77,6 +87,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { void IMessageWithEvents.OnReceiving() { // Verify that the verification code was issued by this authorization server. ErrorUtilities.VerifyProtocol(string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal), Protocol.bad_verification_code); + + // Decrypt the authorizing username. + if (this.User != null) { + // TODO: code here + } } internal static VerificationCode Decode(OAuthWrapAuthorizationServerChannel channel, string value) { diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs index f85cf98..31639f3 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs @@ -17,7 +17,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// A binding element for OAuth 2.0 authorization servers that create/verify /// issued verification codes as part of obtaining access/refresh tokens. /// </summary> - internal class WebAppVerificationCodeBindingElement : IChannelBindingElement { + internal class WebAppVerificationCodeBindingElement : AuthServerBindingElementBase { private const string VerificationCodeContext = "{VerificationCode}"; /// <summary> @@ -27,28 +27,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <summary> - /// Gets or sets the channel that this binding element belongs to. - /// </summary> - /// <remarks> - /// This property is set by the channel when it is first constructed. - /// </remarks> - public Channel Channel { get; set; } - - /// <summary> /// Gets the protection commonly offered (if any) by this binding element. /// </summary> /// <value>Always <c>MessageProtections.None</c></value> /// <remarks> /// This value is used to assist in sorting binding elements in the channel stack. /// </remarks> - public MessageProtections Protection { + public override MessageProtections Protection { get { return MessageProtections.None; } } - protected OAuthWrapAuthorizationServerChannel OAuthChannel { - get { return (OAuthWrapAuthorizationServerChannel)this.Channel; } - } - /// <summary> /// Gets the maximum message age from the standard expiration binding element. /// </summary> @@ -57,14 +45,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <summary> - /// Gets the authorization server hosting this channel. - /// </summary> - /// <value>The authorization server.</value> - private IAuthorizationServer AuthorizationServer { - get { return this.OAuthChannel.AuthorizationServer; } - } - - /// <summary> /// Prepares a message for sending based on the rules of this channel binding element. /// </summary> /// <param name="message">The message to prepare for sending.</param> @@ -76,13 +56,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { var response = message as WebAppSuccessResponse; if (response != null) { - var directResponse = response as IDirectResponseProtocolMessage; - var request = directResponse.OriginatingRequest as WebAppRequest; + var directResponse = (IDirectResponseProtocolMessage)response; + var request = (WebAppRequest)directResponse.OriginatingRequest; - var code = new VerificationCode(this.OAuthChannel, request.Callback, request.Scope); + var code = new VerificationCode(this.OAuthChannel, request.Callback, request.Scope, response.AuthorizingUsername); response.VerificationCode = code.Encode(); return MessageProtections.None; @@ -108,7 +88,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { var request = message as WebAppAccessTokenRequest; if (request != null) { var client = this.AuthorizationServer.GetClient(request.ClientIdentifier); diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs index c92ecc7..fccf067 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs @@ -16,6 +16,12 @@ namespace DotNetOpenAuth.OAuthWrap { [ContractClass(typeof(IAuthorizationServerContract))] public interface IAuthorizationServer { + /// <summary> + /// Gets the client with a given identifier. + /// </summary> + /// <param name="clientIdentifier">The client identifier.</param> + /// <returns>The client registration. Never null.</returns> + /// <exception cref="ArgumentException">Thrown when no client with the given identifier is registered with this authorization server.</exception> IConsumerDescription GetClient(string clientIdentifier); byte[] Secret { get; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs index 9986d19..738846b 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs @@ -49,11 +49,13 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// Initializes a new instance of the <see cref="MessageBase"/> class. /// </summary> /// <param name="request">The originating request.</param> - protected MessageBase(IDirectedProtocolMessage request) { + /// <param name="recipient">The recipient of the directed message. Null if not applicable.</param> + protected MessageBase(IDirectedProtocolMessage request, Uri recipient = null) { Contract.Requires<ArgumentNullException>(request != null); this.originatingRequest = request; this.messageTransport = MessageTransport.Direct; this.version = request.Version; + this.Recipient = recipient; } /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs index e93acca..2cfb017 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// <param name="clientCallback">The client callback.</param> /// <param name="request">The request.</param> internal WebAppSuccessResponse(Uri clientCallback, WebAppRequest request) - : this(clientCallback, ((IMessage)request).Version) { + : base(request, clientCallback) { Contract.Requires<ArgumentNullException>(clientCallback != null, "clientCallback"); Contract.Requires<ArgumentNullException>(request != null, "request"); ((IMessageWithClientState)this).ClientState = ((IMessageWithClientState)request).ClientState; @@ -57,5 +57,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// </value> [MessagePart(Protocol.code, IsRequired = true, AllowEmpty = true)] internal string VerificationCode { get; set; } + + /// <summary> + /// Gets or sets the authorizing user's account name. + /// </summary> + internal string AuthorizingUsername { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs index d5836ff..35417e1 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs @@ -61,6 +61,15 @@ namespace DotNetOpenAuth.OAuthWrap { } /// <summary> + /// Looks up a localized string similar to Client's pre-registered callback URL ({0}) does not match the one found in the authorization request ({1}).. + /// </summary> + internal static string CallbackMismatch { + get { + return ResourceManager.GetString("CallbackMismatch", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}. /// </summary> internal static string CannotObtainAccessTokenWithReason { diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx index 2e70624..ae2cc6c 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx @@ -117,6 +117,9 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> + <data name="CallbackMismatch" xml:space="preserve"> + <value>Client's pre-registered callback URL ({0}) does not match the one found in the authorization request ({1}).</value> + </data> <data name="CannotObtainAccessTokenWithReason" xml:space="preserve"> <value>Failed to obtain access token. Authorization Server reports reason: {0}</value> </data> diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs index b403f91..837d571 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs @@ -40,10 +40,11 @@ namespace DotNetOpenAuth.OAuthWrap { return message; } - public void ApproveAuthorizationRequest(WebAppRequest authorizationRequest, Uri callback = null) { + public void ApproveAuthorizationRequest(WebAppRequest authorizationRequest, string username, Uri callback = null) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); var response = this.PrepareApproveAuthorizationRequest(authorizationRequest, callback); + response.AuthorizingUsername = username; this.Channel.Send(response); } @@ -59,7 +60,6 @@ namespace DotNetOpenAuth.OAuthWrap { return this.TryPrepareAccessTokenResponse(this.Channel.GetRequestFromContext(), out response); } - public bool TryPrepareAccessTokenResponse(HttpRequestInfo httpRequestInfo, out IDirectResponseProtocolMessage response) { Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo"); @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.OAuthWrap { callback = this.GetCallback(authorizationRequest); } - var client = GetClient(authorizationRequest.ClientIdentifier); + var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); var response = new WebAppSuccessResponse(callback, authorizationRequest); return response; } diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs index 4ac1430..0e26994 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs @@ -64,16 +64,14 @@ namespace DotNetOpenAuth.OAuthWrap { return request; } - public IAuthorizationState ProcessUserAuthorization() { - Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext()); - } - - public IAuthorizationState ProcessUserAuthorization(HttpRequestInfo request) { - Contract.Requires<ArgumentNullException>(request != null); + public IAuthorizationState ProcessUserAuthorization(HttpRequestInfo request = null) { Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier)); Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientSecret)); + if (request == null) { + request = this.Channel.GetRequestFromContext(); + } + IMessageWithClientState response; if (this.Channel.TryReadFromRequest<IMessageWithClientState>(request, out response)) { Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.UrlBeforeRewriting, this.Channel.MessageDescriptions.Get(response)); diff --git a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs index 7e04f3f..86a9c5d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs @@ -4,6 +4,8 @@ // </copyright> //----------------------------------------------------------------------- +using DotNetOpenAuth.Messaging; + namespace DotNetOpenAuth.OAuthWrap { using System; using System.Collections.Generic; @@ -30,5 +32,18 @@ namespace DotNetOpenAuth.OAuthWrap { Protocol.HttpAuthorizationHeaderFormat, accessToken); } + + internal static DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); + Contract.Ensures(Contract.Result<DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription>() != null); + + try { + return authorizationServer.GetClient(clientIdentifier); + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); + } catch (ArgumentException ex) { + throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); + } + } } } |