17

When I play ping-pong with the Google OpenID provider, I can't get a friendly name/nickname/username (whatever you want to call it).

I get something atrocious looking like the following:

www.google.com/accounts/o8/id?id=AItOawmtAnQvSXGhRTkAHZWyxi4L4EKa7xoTT1dk  

instead of something nice like

JohnDoe

What's the protocol for getting the user's name from Google in a nice friendly manner like say, myopenid does?

**I'm using DotNetOpenAuth*

MunkiPhD
  • 3,636
  • 1
  • 29
  • 51

3 Answers3

13

You can't. The identifier that the OP issues is strictly up to the OP. The RP doesn't really have any say in it. Now, some OPs support offering attributes with the login, such as nickname, email address, etc. Google has very limited support for these, offering only email address.

Google chose to not issue user-recognizable identifiers because it's an information disclosure risk. Yahoo went both routes by offering users both a human-friendly and non-human-friendly identifiers that the user can choose between. MyOpenID and other OPs generally go with just a user-friendly identifier that the user picks when they sign up at the OP.

You may want to special case Google at your RP to pick a more friendly string to display to the user when they're logged in, or since Google isn't the only one that does this, write code to figure out when the identifier is unreadable and display something more friendly to the user so they know they're logged in (perhaps their email address or a nickname they pick on your site).

Caution: if you choose to display a more friendly identifier than the one Google issues, you must still use the official Claimed Identifier from Google for the official username of the user that you pass to FormsAuthentication.RedirectFromLogin and for username lookup in your database. Anything else you put together usually introduces security risks.

Andrew Arnott
  • 80,040
  • 26
  • 132
  • 171
  • Thanks for clearing this up. I guess using a default friendly identifier as the user's email can work. Would you happen to know which other services don't play nice with regards to friendly names? – MunkiPhD Aug 31 '09 at 00:58
  • Yahoo is the only other one I know of, and it only has this behavior sometimes. – Andrew Arnott Aug 31 '09 at 04:02
  • 1
    Andrew, I managed to get deeper into your blog posts and found the one with regards to Yahoo. I'll have to read it again to fully comprehend it - but in my unknowing trials I could not even get an email address back from Yahoo, which befuddles me as I use a Yahoo login for stackoverflow - and my information clearly transferred over... – MunkiPhD Aug 31 '09 at 16:00
10

base on Roy answer, i tried to make the same request using DotNetOpenAuth and it worked fine. the request:

var req = openid.CreateRequest("https://www.google.com/accounts/o8/id");
var fetch = new FetchRequest();
fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Contact.Email,true));
fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.First,true));
fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.Last,true));

req.AddExtension(fetch);

note: make sure the second parm of AttributeRequest constructor is set to true.

the response part is straight forward.

var openid = new OpenIdRelyingParty();
var response = openid.GetResponse();
var fetch = response.GetExtension<FetchResponse>();
if (fetch != null) {
IList<string> emailAddresses =fetch.Attributes[WellKnownAttributes.Contact.Email].Values;
IList<string> firstNames = fetch.Attributes[WellKnownAttributes.Name.First].Values;
IList<string> lastName = fetch.Attributes[WellKnownAttributes.Name.Last].Values;
}
Charles Menguy
  • 40,830
  • 17
  • 95
  • 117
Aymen
  • 422
  • 5
  • 10
  • Depending on how you're using this (I'm using OWIN) you might not have WellKnownAttributes in the set of things installed in your project, and you probably don't want to add DotNetOpenAuth just for this. Here are the OpenId strings in their WellKnownAttributes (and elsewhere in other OpenId libs): first name: "given_name", last name: "family_name", middle name: "middle_name", nickname: "nickname" – Chris Moschini May 22 '16 at 17:02
6

As of 2012, it seems that the Google OpenID endpoint supports first and last name retrieval through the Attribute Exchange protocol. Here is some example Python code using the Pyramid web framework and Janrain's python-openid package.

from openid.consumer import consumer
from openid.extensions.ax import AttrInfo, FetchRequest, FetchResponse
from openid.store.filestore import FileOpenIDStore
from openid.store.sqlstore import PostgreSQLStore, MySQLStore, SQLiteStore

AX_FIRSTNAME = 'http://axschema.org/namePerson/first'
AX_LASTNAME = 'http://axschema.org/namePerson/last'
AX_EMAIL = 'http://axschema.org/contact/email'

@view_config(route_name='openID_start', permission=NO_PERMISSION_REQUIRED)
def start(request):
    'Start openID authentication process'
    params = request.params
    openIDURL = params.get('openIDURL')
    if not openIDURL:
        return HTTPResponse('Parameter expected: openIDURL')
    openIDConsumer = get_consumer(request)
    try:
        openIDRequest = openIDConsumer.begin(openIDURL)
    except consumer.DiscoveryFailure, error:
        return HTTPResponse('Discovery failed: %s' % escape(error))
    else:
        if not openIDRequest:
            return HTTPResponse('Not an openID provider: %s' % escape(openIDURL))

        axRequest = FetchRequest()
        axRequest.add(AttrInfo(AX_FIRSTNAME, required=True))
        axRequest.add(AttrInfo(AX_LASTNAME, required=True))
        axRequest.add(AttrInfo(AX_EMAIL, required=True))
        openIDRequest.addExtension(axRequest)

        sourceURL = request.host_url
        targetURL = request.route_url('openID_finish')
        if openIDRequest.shouldSendRedirect():
            return HTTPFound(location=openIDRequest.redirectURL(sourceURL, targetURL))
        return HTTPResponse(openIDRequest.htmlMarkup(sourceURL, targetURL))

@view_config(route_name='openID_finish', permission=NO_PERMISSION_REQUIRED)
def finish(request):
    'Finish openID authentication process'
    openIDConsumer = get_consumer(request)
    targetURL = request.route_url('openID_finish')
    openIDResponse = openIDConsumer.complete(request.params, targetURL)
    html = openIDResponse.status + '<br>'
    for key, value in openIDResponse.__dict__.iteritems():
        html += '%s: %s<br>' % (escape(key), escape(value))
    html += '<br>'
    if consumer.SUCCESS == openIDResponse.status:
        axResponse = FetchResponse.fromSuccessResponse(openIDResponse)
        html += 'First name: %s<br>' % escape(axResponse.get(AX_FIRSTNAME))
        html += 'Last name: %s<br>' % escape(axResponse.get(AX_LASTNAME))
        html += 'Email: %s<br>' % escape(axResponse.get(AX_EMAIL))
    return HTTPResponse(html)

def get_consumer(request):
    try:
        openIDStore = {
            'sqlite': SQLiteStore,
            'postgresql': PostgreSQLStore,
            'mysql': MySQLStore,
        }[db.bind.name](db.bind.raw_connection())
    except KeyError:
        openIDStore = FileOpenIDStore('data/openIDs')
    try:
        openIDStore.createTables()
    except:
        pass
    return consumer.Consumer(request.session, openIDStore)
Roy Hyunjin Han
  • 4,725
  • 2
  • 30
  • 23