This is post #13 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.
I need an OpenID selector control on my login screen to make it easier for users to enter their OpenID identifiers. The most popular one around seems to be openid-selector or some variation of it. While a comprehensive control, I had a play with the demo and found a few things I didn't love out of the box.
Two and three can probably be fixed with configuration, but there are no easy examples on how to do that on the openid-selector site. I could get openid-selector to play nice, or I could roll my own minimalist version with jQuery. One guess which way I went.
I want to keep my HTML light and easy to understand.
<h2>Login</h2>
<div id="openid" class="append-bottom">
<p>
Please click your OpenID provider or enter your OpenID identifier below.
</p>
<div>
<a id="google"></a>
<a id="yahoo"></a>
<a id="aol"></a>
<a id="myopenid"></a>
</div>
<div class="clear">
<a id="livejournal"></a>
<a id="wordpress"></a>
<a id="blogger"></a>
<a id="verisign"></a>
<a id="claimid"></a>
<a id="clickpass"></a>
<a id="google_profile"></a>
</div>
<div class="clear">
</div>
</div>
<%
using (Html.BeginForm("authenticate", "user", new { ReturnUrl = Request["ReturnUrl"] }, FormMethod.Post))
{%>
<p id="openid_username_block">
<label for="openid_username"></label><br />
<input id="openid_username" name="openid_username" type="text" />
</p>
<p>
<label for="openid_identifier">OpenID</label><br />
<input id="openid_identifier" name="openid_identifier" type="text" />
</p>
<p>
<input type="submit" value="Login" />
</p>
<%
}%>
The idea is to hide the #openid
block until everything is formatted, loaded and ready to use. Each anchor will be an OpenID provider button. The anchors in the first block will be large buttons, and the anchors in the second block will be small buttons. I want to be able to easily add providers to either the top or bottom.
The #openid_username
input is for the OpenID providers whose identifiers are based on a username. The #openid_username_block
block will be hidden initially too. I included it in the form so that the user can just hit enter when they're done.
There's a little CSS to hide the two blocks mentioned above. The rest of the CSS, heavily influenced by openid-selector, formats the buttons.
a.openid-large
{
background-position: center center;
background-repeat: no-repeat;
border: 1px solid #ddd;
float: left;
height: 60px;
margin: 0 5px 5px 0;
width: 100px;
}
a.openid-small
{
background-position: center center;
background-repeat: no-repeat;
border: 1px solid #ddd;
float: left;
height: 24px;
margin-right: 5px;
width: 24px;
}
div#openid
{
display: none;
}
p#openid_username_block
{
display: none;
}
An array is a handy way to define the important bits about each provider.
var providers = {
google : {
name : 'Google',
username : false,
url: 'https://www.google.com/accounts/o8/id'
},
yahoo : {
name : 'Yahoo',
username : false,
url: 'http://me.yahoo.com/'
},
aol : {
name : 'AOL',
username : true,
url: 'http://openid.aol.com/{username}'
},
// ...
}
The first thing to do is format everything, get the click events defined, and then display the #openid
block.
$(document).ready(function () {
$('div#openid a').attr('href', function () { return 'javascript:openid(\'' + this.id + '\');' });
$('div#openid a').attr('title', function () { return providers[this.id].name });
$('div#openid div').first().children('a').addClass('openid-large').parent().next().children('a').addClass('openid-small');
$('div#openid a').css('background-image', function () { return 'url("<%=Url.Content("~/content/openid/")%>' + this.id + '.gif")' });
$('input#openid_identifier').focus();
$('div#openid').css('display', 'inherit');
});
The first line sets the href
of the anchors to a JavaScript method I'll display shortly. The second line sets the title of each anchor to the name of the provider. The third line adds classes to the anchors -- openid-large
for the anchors in the first div and openid-small
for the anchors in the second div. The fourth line sets the background image of each anchor. I use an MVC convenience method, Url.Content
to get the correct path for the images. The fifth and sixth lines set the focus and reveal the result!
So what about the magic? What happens when a user clicks on a button?
function openid(id) {
var provider = providers[id];
var username_block = $('p#openid_username_block');
var username = $('input#openid_username');
var input = $('input#openid_identifier');
var form = input.parents('form');
if (provider.username) {
username_block.css('display', 'inherit');
username_block.children('label').html(provider.name + ' Username');
username.keypress(function () { setTimeout(function () { input.val(provider.url.replace('{username}', username.val())) }, 500) });
username.focus();
input.val(provider.url.replace('{username}', username.val()));
}
else {
username_block.css('display', 'none');
input.val(providers[id].url);
input.focus();
form.submit();
}
};
Just what you would expect. If the user clicks on a provider that includes a username in the identifier, the #openid_username_block
block is shown, the label is set, and the keypress event of the #openid_username
input is set to update the #openid_identifier
input automagically. That same update operation is also called once in case there is already a value in the #openid_username
input -- handy if the user clicks the wrong button but doesn't realise it until after entering their username.
If the user hits Google or Yahoo!, the two providers that don't require a username, the form is submitted automatically.
In the end it wasn't that hard to roll my own simple OpenID selector. The basics are there, including a graceful degradation if JavaScript is disabled, and a decent amount of flexibility for maintaining existing providers or adding new providers when they arise. Test it out for yourself. Login with an OpenID -- there's no need to actually register with my site if you don't want to.
There are 0 comments.
Newer
Speed Bumps
Newer
Speed Bumps
browse with Pivot
Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.