|
More and more I see sites supporting OpenID as Authentication mechanism. I’m for example a user of sites like: I need to read this, Get Satisfaction and Google Login more or less.
To support my own family I set up OpenID on my own domain, http://openid.mymonster.nl/ hosted by MyOpenID. This just works like a charm. For the purpose of this article I created a test identity at my MyOpenID. I suggest everyone doing development for openid connectivity to create a test identity, I don’t want to mess with my real OpenID identity. This is part one of a three part series on the creation of an OpenID User Control. I initially created the control for use in my own application and have submitted it to the Silverlight Control Builder Contest of 2009.
UI Design
One of the first things I was thinking about, was my design capacities. I came up with the following design, before signing in.
After you have signed in.
Yes I know, it’s very straightforward, and all the designers in this world could have thought about a better alternative. That’s why I thought this control to require the ability to template it. There are some articles on the web about templating Silverlight User Controls, this one helped me a lot.
To support Templating for a user control it needs to inherit ContentControl
Visually I identified two states:
- Unauthenticated – This state is active when the user hasn’t signed in yet.
- Authenticated – This state is active when the user has successfully signed in.
If the sign in was successful the control will move to the authenticated state. These Visual States can be used in the Xaml part of the user control, which we will do later on.
As you can see in the above pictures we can think about three essential parts in this control.
- LoginButton, typeof(Button)
- IdentityInput, typeof(TextBox)
- IdentitySuccessLabel, typeof(TextBlock)
When you combine just these parts the C# file will look like this. (Please note, some parts are left out for the clear picture).
[TemplatePart(Name = LoginButton, Type = typeof(Button))]
[TemplatePart(Name = IdentityInput, Type = typeof(TextBox))]
[TemplatePart(Name = IdentitySuccessLabel, Type = typeof(TextBlock))]
[TemplateVisualState(Name = VisualStates.Unauthenticated, GroupName = VisualStates.CommonStates)]
[TemplateVisualState(Name = VisualStates.Authenticated, GroupName = VisualStates.CommonStates)]
public class OpenIdLoginControl : ContentControl
{
private const string IdentityInput = "IdentityInput";
private const string IdentitySuccessLabel = "IdentitySuccessLabel";
private const string LoginButton = "LoginButton";
private TextBox m_identityInput;
private TextBlock m_identitySuccessLabel;
private Button m_loginButton;
public OpenIdLoginControl()
{
DefaultStyleKey = typeof(OpenIdLoginControl);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
m_identityInput = (TextBox)GetTemplateChild(IdentityInput);
m_loginButton = (Button)GetTemplateChild(LoginButton);
m_identitySuccessLabel = (TextBlock)GetTemplateChild(IdentitySuccessLabel);
}
#region Nested type: VisualStates
private static class VisualStates
{
internal const string Authenticated = "Authenticated";
internal const string CommonStates = "CommonStates";
internal const string Unauthenticated = "Unauthenticated";
}
#endregion
}
To give this control a default style there needs to be a Themes directory and a xaml-file called Generic.xaml inside the library which will contain this control. The Generic.xaml file is a ResourceDictionary, and in this case we add a style for the OpenIdLoginControl. The xaml file kind of looks like the following (I removed the VisualStateManager parts for the VisualStates).
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:openid="clr-namespace:MM.OpenId.Controls">
<Style TargetType="openid:OpenIdLoginControl">
<Setter Property="Width" Value="330" />
<Setter Property="Height" Value="50" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="openid:OpenIdLoginControl">
<Border BorderBrush="Black" CornerRadius="4" BorderThickness="1">
<Grid x:Name="LayoutRoot" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.Row="0" Source="/MM.OpenId.Controls;component/openid-icon.png" Width="30" Height="30" Margin="8"/>
<TextBlock Grid.Column="1" Grid.Row="0" x:Name="IdentitySuccessLabel" VerticalAlignment="Center" Margin="8" HorizontalAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="0" x:Name="IdentityInput" Text="http://openid.mymonster.nl/demo" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="8" />
<Button Grid.Column="2" Grid.Row="0" x:Name="LoginButton" Content="Sign In" Margin="8" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Because this is just the default template, you can change this to adjust it to your own application.
Because the extreme size of this article I’ve split this article into multiple articles. I will provide the full source at the end of the last article. You can expect at least the following two parts:
- OpenID Integration
- Integration in your own application
Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.
|
July 28th, 2009 at 21:17
[...] This post was Twitted by Mark_Monster [...]
July 29th, 2009 at 06:45
[...] from Mark Monster [...]
July 29th, 2009 at 07:04
I’m a little unclear… does your control actually perform the OpenID authentication, or just display its status? If you actually do the OpenID auth here (on the client), how can the server validate it in order to provide access to protected resources?
July 29th, 2009 at 07:25
Hi Andrew,
To anticipate on what will happen in the next parts.
Yes the control will perform the OpenID authentication. My idea is to have the client-application use the authenticated user as one of the parameters while making server-side calls.
Do you think re-authentication on the server is needed? I thought most of the time the authenticated user is cached (during a session) after the initial login. My approach will be similar except the caching will be done at client level (Silverlight).
The next part in my this series will explain how I will do authentication against the OpenID providers. I’m sure you have more knowledge and experience with OpenID, maybe you can suggest approaches that will make it more secure.
Thanks
July 29th, 2009 at 08:31
Hi Mark,
A Silverlight OpenID login control has been a popular request for the DotNetOpenAuth library (http://dotnetopenauth.uservoice.com/pages/14800-general/suggestions/146009-create-a-silverlight-openid-control). However I’ve never been able to imagine a secure way to do it.
When it comes to security, you MUST start by assuming the client has been completely hijacked or substituted. For instance, when that request comes in from your Silverlight app/control to the server saying “Ok, this user is X and he wants to do Y”, how can you possibly trust it? Even if you “signed” it on the client side to prevent forgery, since the signing code is already on the client, it can be studied, the secrets stolen, and the signature forged.
The way sessions work w/o Silverlight is that the server does the OpenID authentication verification, and then the server issues an HTTP cookie to the client that the client uses to say “I’m X, and I want to do Y”. But the key that makes it work it that the server issued that cookie in the first place, signing it or whatever, and therefore can trust it.
Now, you COULD do SOME OpenID work on the client. For example, you could perform identifier discovery from within Silverlight in an effort to make login a bit faster, but then you’re left with only a couple of options. Either a redirect or a popup window must follow in order to allow authentication to take place. A redirect would take out your Silverlight control because the page is gone. A popup seems to be the way things are going though, so let’s assume you’re using a popup so that your Silverlight control stays alive during authentication. When the positive assertion (successful auth) comes back from the Provider, what can the Silverlight control do with that? Well, it could validate the signature on the assertion itself, but since there’s a nonce in there that would invalidate it so your server couldn’t double-check. So the way I do it with my AJAX control is that the AJAX control blissfully assumes the positive assertion isn’t forged until it needs to access a resource from the web server. Then it sends the positive assertion along with the request to the server so the web server can then validate the positive assertion, and send the protected resource and an HTTP authentication ticket cookie down to the client.
Now you have a couple of options if you go this route: you can go with what’s called stateless (or ‘dumb’) mode, meaning your web server will always have to send an extra message to the Provider to verify the signature, or you can use stateful mode so the web server can verify the signature immediately. But stateful mode would require that even if your Silverlight control did the discovery on its own, it would have to communicate with your server to find out which association handle to use in the checkid_setup (auth request) message.
So the long and short of it is… you haven’t gained any speed increase, and you haven’t avoided the need for a popup window or redirect. Perhaps the user experience looks more slick because of whatever Silverlight rendering you might be doing, and that’s definitely worth something. But if you go that route, since making it complicated and doing OpenID auth on the Silverlight side doesn’t really buy you anything in my opinion, just keep your control simple UI level, and let the server do all the work using tried, proven, and tested OpenID libraries that run on the server.
Getting OpenID *right* is a huge task, where “right” includes interoperability tested, security tested and reviewed, and feature rich. A Silverlight control UI would be a neat extra feature that DotNetOpenAuth may someday incorporate, but for the reasons I’ve listed above it’s not likely to happen at least in the near future.
That all said, I’m interested in looking at whatever you end up with. Please ping me when you’ve got the next thing going.
July 30th, 2009 at 15:05
Hi, take a look at my stylish silverlight theme for all controls http://www.xamltemplates.net/sl/
September 11th, 2009 at 22:04
[...] already a long time ago when I posted part 1 of the OpenID User Control, but sadly I didn’t have any time to blog, until [...]
September 13th, 2009 at 00:00
OpenID User Control in Silverlight – Part 1 UI Design…
DotNetBurner - burning hot .net content…
September 15th, 2009 at 10:39
OpenID User Control in Silverlight – Part 1 UI Design…
Thank you for submitting this cool story - Trackback from progg.ru…
September 16th, 2009 at 15:06
[...] UI Design [...]