summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-06-02 22:39:46 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2010-06-02 22:39:46 -0700
commitccbec1badf616062cf9cb102ae4a0b2d835610b0 (patch)
tree11037cf8c3edbbd444bdccfc2efe6dad82027bd6
parent9db9a3768ce97f565ff01ca355be295d9775c4ef (diff)
downloadDotNetOpenAuth-ccbec1badf616062cf9cb102ae4a0b2d835610b0.zip
DotNetOpenAuth-ccbec1badf616062cf9cb102ae4a0b2d835610b0.tar.gz
DotNetOpenAuth-ccbec1badf616062cf9cb102ae4a0b2d835610b0.tar.bz2
OAuth 2.0 web flow now works, client, auth server, and resource server, in the sample!
Yay.
-rw-r--r--samples/OAuthConsumer/SampleWcf.aspx2
-rw-r--r--samples/OAuthConsumer/SampleWcf.aspx.cs8
-rw-r--r--samples/OAuthConsumer/SampleWcf2.aspx12
-rw-r--r--samples/OAuthConsumer/SampleWcf2.aspx.cs62
-rw-r--r--samples/OAuthConsumer/SampleWcf2.aspx.designer.cs54
-rw-r--r--samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs2
-rw-r--r--samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs53
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs10
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs3
11 files changed, 180 insertions, 30 deletions
diff --git a/samples/OAuthConsumer/SampleWcf.aspx b/samples/OAuthConsumer/SampleWcf.aspx
index fb318ce..fcec089 100644
--- a/samples/OAuthConsumer/SampleWcf.aspx
+++ b/samples/OAuthConsumer/SampleWcf.aspx
@@ -1,4 +1,4 @@
-<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" Inherits="OAuthConsumer.SampleWcf" Codebehind="SampleWcf.aspx.cs" %>
+<%@ Page Title="OAuth 1.0a consumer" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" Inherits="OAuthConsumer.SampleWcf" Codebehind="SampleWcf.aspx.cs" %>
<asp:Content ID="Content2" ContentPlaceHolderID="Body" runat="Server">
<fieldset title="Authorization">
diff --git a/samples/OAuthConsumer/SampleWcf.aspx.cs b/samples/OAuthConsumer/SampleWcf.aspx.cs
index 489e294..74c6e6a 100644
--- a/samples/OAuthConsumer/SampleWcf.aspx.cs
+++ b/samples/OAuthConsumer/SampleWcf.aspx.cs
@@ -39,10 +39,10 @@
string[] scopes = (from item in this.scopeList.Items.OfType<ListItem>()
where item.Selected
select item.Value).ToArray();
- string scope = string.Join("|", scopes);
+ string scope = string.Join(" ", scopes);
var requestParams = new Dictionary<string, string> {
- { "scope", scope },
- };
+ { "scope", scope },
+ };
var response = consumer.PrepareRequestUserAuthorization(callback.Uri, requestParams, null);
consumer.Channel.Send(response);
}
@@ -83,7 +83,7 @@
WebConsumer consumer = this.CreateConsumer();
WebRequest httpRequest = consumer.PrepareAuthorizedRequest(serviceEndpoint, accessToken);
- HttpRequestMessageProperty httpDetails = new HttpRequestMessageProperty();
+ var httpDetails = new HttpRequestMessageProperty();
httpDetails.Headers[HttpRequestHeader.Authorization] = httpRequest.Headers[HttpRequestHeader.Authorization];
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel)) {
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpDetails;
diff --git a/samples/OAuthConsumer/SampleWcf2.aspx b/samples/OAuthConsumer/SampleWcf2.aspx
index e8d672b..797a2fc 100644
--- a/samples/OAuthConsumer/SampleWcf2.aspx
+++ b/samples/OAuthConsumer/SampleWcf2.aspx
@@ -1,4 +1,4 @@
-<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" Inherits="OAuthConsumer.SampleWcf2" Codebehind="SampleWcf2.aspx.cs" %>
+<%@ Page Title="OAuth 2.0 client (web server flow)" 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">
@@ -10,4 +10,14 @@
<asp:Button ID="getAuthorizationButton" runat="server" Text="Get Authorization" OnClick="getAuthorizationButton_Click" />
<asp:Label ID="authorizationLabel" runat="server" />
</fieldset>
+ <br />
+ <asp:Button ID="getNameButton" runat="server" Text="Get Name" OnClick="getNameButton_Click" />
+ <asp:Label ID="nameLabel" runat="server" />
+ <br />
+ <asp:Button ID="getAgeButton" runat="server" Text="Get Age" OnClick="getAgeButton_Click" />
+ <asp:Label ID="ageLabel" runat="server" />
+ <br />
+ <asp:Button ID="getFavoriteSites" runat="server" Text="Get Favorite Sites"
+ onclick="getFavoriteSites_Click" />
+ <asp:Label ID="favoriteSitesLabel" runat="server" />
</asp:Content> \ No newline at end of file
diff --git a/samples/OAuthConsumer/SampleWcf2.aspx.cs b/samples/OAuthConsumer/SampleWcf2.aspx.cs
index 09ebd12..fa7a102 100644
--- a/samples/OAuthConsumer/SampleWcf2.aspx.cs
+++ b/samples/OAuthConsumer/SampleWcf2.aspx.cs
@@ -1,16 +1,27 @@
namespace OAuthConsumer {
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.Linq;
+ using System.Net;
+ using System.ServiceModel;
+ using System.ServiceModel.Channels;
+ using System.ServiceModel.Security;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.OAuthWrap;
+ using OAuthConsumer.SampleServiceProvider;
public partial class SampleWcf2 : System.Web.UI.Page {
private static InMemoryClientTokenManager TokenManager = new InMemoryClientTokenManager();
+ private static IAuthorizationState Authorization {
+ get { return (AuthorizationState)HttpContext.Current.Session["Authorization"]; }
+ set { HttpContext.Current.Session["Authorization"] = value; }
+ }
+
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack)
{
@@ -18,7 +29,7 @@
var authorization = client.ProcessUserAuthorization();
if (authorization != null)
{
- Response.Write("Obtained access token: " + authorization.AccessToken);
+ Authorization = authorization;
}
}
}
@@ -27,7 +38,7 @@
string[] scopes = (from item in this.scopeList.Items.OfType<ListItem>()
where item.Selected
select item.Value).ToArray();
- string scope = string.Join("|", scopes);
+ string scope = string.Join(" ", scopes);
var client = CreateClient();
string clientState;
@@ -36,13 +47,56 @@
client.Channel.Send(response);
}
+ protected void getNameButton_Click(object sender, EventArgs e) {
+ try {
+ this.nameLabel.Text = CallService(client => client.GetName());
+ } catch (SecurityAccessDeniedException) {
+ this.nameLabel.Text = "Access denied!";
+ }
+ }
+
+ protected void getAgeButton_Click(object sender, EventArgs e) {
+ try {
+ int? age = CallService(client => client.GetAge());
+ this.ageLabel.Text = age.HasValue ? age.Value.ToString(CultureInfo.CurrentCulture) : "not available";
+ } catch (SecurityAccessDeniedException) {
+ this.ageLabel.Text = "Access denied!";
+ }
+ }
+
+ protected void getFavoriteSites_Click(object sender, EventArgs e) {
+ try {
+ string[] favoriteSites = CallService(client => client.GetFavoriteSites());
+ this.favoriteSitesLabel.Text = string.Join(", ", favoriteSites);
+ } catch (SecurityAccessDeniedException) {
+ this.favoriteSitesLabel.Text = "Access denied!";
+ }
+ }
+
+ private T CallService<T>(Func<DataApiClient, T> predicate) {
+ DataApiClient client = new DataApiClient();
+ //var serviceEndpoint = new MessageReceivingEndpoint(client.Endpoint.Address.Uri, HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest);
+ if (Authorization == null) {
+ throw new InvalidOperationException("No access token!");
+ }
+
+ var httpRequest = (HttpWebRequest)WebRequest.Create(client.Endpoint.Address.Uri);
+ WebAppClient.AuthorizeRequest(httpRequest, Authorization);
+
+ var httpDetails = new HttpRequestMessageProperty();
+ httpDetails.Headers[HttpRequestHeader.Authorization] = httpRequest.Headers[HttpRequestHeader.Authorization];
+ using (OperationContextScope scope = new OperationContextScope(client.InnerChannel)) {
+ OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpDetails;
+ return predicate(client);
+ }
+ }
+
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)
- {
+ var client = new WebAppClient(authServerDescription) {
ClientIdentifier = "sampleconsumer",
ClientSecret = "samplesecret",
TokenManager = TokenManager,
diff --git a/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs b/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs
index 3046936..f42efff 100644
--- a/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs
+++ b/samples/OAuthConsumer/SampleWcf2.aspx.designer.cs
@@ -38,5 +38,59 @@ namespace OAuthConsumer {
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Label authorizationLabel;
+
+ /// <summary>
+ /// getNameButton 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 getNameButton;
+
+ /// <summary>
+ /// nameLabel 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 nameLabel;
+
+ /// <summary>
+ /// getAgeButton 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 getAgeButton;
+
+ /// <summary>
+ /// ageLabel 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 ageLabel;
+
+ /// <summary>
+ /// getFavoriteSites 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 getFavoriteSites;
+
+ /// <summary>
+ /// favoriteSitesLabel 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 favoriteSitesLabel;
}
}
diff --git a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
index ed70a15..75778f5 100644
--- a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
+++ b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
@@ -13,7 +13,7 @@
internal class OAuth2AuthorizationServer : IAuthorizationServer {
private static readonly byte[] secret;
- private static readonly RSAParameters asymmetricKey;
+ internal static readonly RSAParameters asymmetricKey;
private readonly INonceStore nonceStore = new DatabaseNonceStore();
diff --git a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs
index b12e85a..a48288a 100644
--- a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs
+++ b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs
@@ -9,6 +9,8 @@
using System.ServiceModel.Security;
using DotNetOpenAuth;
using DotNetOpenAuth.OAuth;
+ using DotNetOpenAuth.OAuthWrap;
+ using DotNetOpenAuth.OAuth.ChannelElements;
/// <summary>
/// A WCF extension to authenticate incoming messages using OAuth.
@@ -24,13 +26,10 @@
HttpRequestMessageProperty httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
Uri requestUri = operationContext.RequestContext.RequestMessage.Properties["OriginalHttpRequestUri"] as Uri;
- ServiceProvider sp = Constants.CreateServiceProvider();
- try {
- var auth = sp.ReadProtectedResourceAuthorization(httpDetails, requestUri);
- if (auth != null) {
- var accessToken = Global.DataContext.OAuthTokens.Single(token => token.Token == auth.AccessToken);
- var principal = sp.CreatePrincipal(auth);
+ try {
+ var principal = VerifyOAuth2(httpDetails, requestUri);
+ if (principal != null) {
var policy = new OAuthPrincipalAuthorizationPolicy(principal);
var policies = new List<IAuthorizationPolicy> {
policy,
@@ -46,14 +45,13 @@
}
securityContext.AuthorizationContext.Properties["Identities"] = new List<IIdentity> {
- principal.Identity,
- };
+ principal.Identity,
+ };
// Only allow this method call if the access token scope permits it.
- string[] scopes = accessToken.Scope.Split('|');
- if (scopes.Contains(operationContext.IncomingMessageHeaders.Action)) {
- return true;
- }
+ return principal.IsInRole(operationContext.IncomingMessageHeaders.Action);
+ } else {
+ return false;
}
} catch (ProtocolException ex) {
Global.Logger.Error("Error processing OAuth messages.", ex);
@@ -61,5 +59,36 @@
return false;
}
+
+ private OAuthPrincipal VerifyOAuth1(HttpRequestMessageProperty httpDetails, Uri requestUri) {
+ ServiceProvider sp = Constants.CreateServiceProvider();
+ var auth = sp.ReadProtectedResourceAuthorization(httpDetails, requestUri);
+ if (auth != null) {
+ var accessToken = Global.DataContext.OAuthTokens.Single(token => token.Token == auth.AccessToken);
+ var principal = sp.CreatePrincipal(auth);
+ return principal;
+ }
+
+ return null;
+ }
+
+ private OAuthPrincipal VerifyOAuth2(HttpRequestMessageProperty httpDetails, Uri requestUri) {
+ // for this sample where the auth server and resource server are the same site,
+ // we use the same public/private key.
+ var resourceServer = new ResourceServer(
+ new StandardAccessTokenAnalyzer(
+ OAuth2AuthorizationServer.asymmetricKey,
+ OAuth2AuthorizationServer.asymmetricKey));
+
+ string username, scope;
+ var error = resourceServer.VerifyAccess(new DotNetOpenAuth.Messaging.HttpRequestInfo(httpDetails, requestUri), out username, out scope);
+ if (error == null) {
+ string[] scopes = scope.Split(new char[] { ' ' });
+ var principal = new OAuthPrincipal(username, scopes);
+ return principal;
+ } else {
+ return null;
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
index 025ef09..48fe813 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
@@ -51,7 +51,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <param name="username">The username.</param>
/// <param name="roles">The roles this user belongs to.</param>
- internal OAuthPrincipal(string username, string[] roles)
+ public OAuthPrincipal(string username, string[] roles)
: this(new OAuthIdentity(username), roles) {
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs
index 1f2efaa..dfdacb7 100644
--- a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs
@@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
/// </summary>
/// <param name="version">The version.</param>
/// <param name="recipient">The recipient.</param>
- internal AccessProtectedResourceRequest(Version version, Uri recipient)
+ internal AccessProtectedResourceRequest(Uri recipient, Version version)
: base(version, MessageTransport.Direct, recipient) {
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
index 5eaa1e5..3797cba 100644
--- a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
@@ -14,13 +14,15 @@ namespace DotNetOpenAuth.OAuthWrap {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuthWrap.ChannelElements;
- internal class StandardAccessTokenAnalyzer : IAccessTokenAnalyzer {
- internal StandardAccessTokenAnalyzer() {
+ public class StandardAccessTokenAnalyzer : IAccessTokenAnalyzer {
+ public StandardAccessTokenAnalyzer(RSAParameters authorizationServerPublicSigningKey, RSAParameters resourceServerPrivateEncryptionKey) {
+ this.AuthorizationServerPublicSigningKey = authorizationServerPublicSigningKey;
+ this.ResourceServerPrivateEncryptionKey = resourceServerPrivateEncryptionKey;
}
- internal RSAParameters AuthorizationServerPublicSigningKey { get; set; }
+ public RSAParameters AuthorizationServerPublicSigningKey { get; private set; }
- internal RSAParameters ResourceServerPrivateEncryptionKey { get; set; }
+ public RSAParameters ResourceServerPrivateEncryptionKey { get; private set; }
public bool TryValidateAccessToken(string accessToken, out string user, out string scope) {
var token = AccessToken.Decode(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey, accessToken);
diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs
index 1847886..e351198 100644
--- a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs
@@ -52,7 +52,8 @@ namespace DotNetOpenAuth.OAuthWrap {
Contract.Ensures(Contract.Result<WebAppRequest>().Callback == authorization.Callback);
if (authorization.Callback == null) {
- authorization.Callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting;
+ authorization.Callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting
+ .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(WebAppSuccessResponse), Protocol.Default.Version));
authorization.SaveChanges();
}