OAuth Support in Mail Applications

The AuthService APIs help you make web service to endpoints that require OAuth. Developers configure their app with the consumer key and secret, then use these APIs to handle the authorization flow as well as storing of tokens and signing of the webservice calls.

AuthService supports both access to public data (two-legged OAuth) and private data (three-legged OAuth). More information about the difference can be found here. Yahoo! Mail's AuthService APIs also automatically refresh OAuth tokens in accordance with the OAuth Session Draft.

Endpoint Flash Security Requirement

Whether you're making authenticated webservice calls using AuthService.callWebService or making unauthenticated calls using Application.callWebService, the host your are calling must be configured to allow flash clients. This is because we proxy web service calls using Adobe flash. Your endpoint must allow web service calls originating from at least the following domains:

  • yahoo.com
  • yimg.com


Adobe's flash developer documentation details how to allow this by installing a crossdomain.xml file on your endpoint's webserver. It must be served with the content-type set to either text/xml or text/x-cross-domain-policy. Below is an example crossdomain.xml that would work.

  1. <?xml version="1.0"?>
  2. <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
  3. <cross-domain-policy>
  4. <allow-access-from domain="*.yimg.com" secure="false"/>
  5. <allow-access-from domain="*.yahoo.com" secure="false"/>
  6. </cross-domain-policy>
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*.yimg.com" secure="false"/>
  <allow-access-from domain="*.yahoo.com" secure="false"/>
</cross-domain-policy>

The secure="false" attribute in the above example is not strictly necessary: it allows webservice calls to be made over https by telling Flash that it's okay to make https:// requests using code loaded from an http:// address.

Flash-less support

If Flash presence is not detected in the browser, an attempt is made to make an XHR call to the server. Cross-origin resource sharing (CORS) is a feature supported by modern browsers but also requires server settings. If the browser does not support CORS or the server is not set up to accept cross-origin calls, we fall back to showing the 'Flash required' message.

On the client this is handled transparently by the *.callWebService call. For example, the YQL call to Delicious in the kitchen sink sample app will go through CORS when in a Flash-less environment.

The server should be configured to allow access from *.yahoo.com. Please see http://enable-cors.org/ for more information about server settings for CORS.

Auth Service Configuration File

When you create an app with ymdt, the source directory includes a file called auth.xml. Yahoo! Mail looks at this file while bootstrapping your app to figure out what authenticated web services your app might talk to.

Here's an example auth.xml configuration:

  1. <?xml version="1.0"?>
  2. <Application>
  3. <Service name="yahoo" type="OAuth">
  4. <Consumer key="..." secret="..."/>
  5. <Request url="https://api.login.yahoo.com/oauth/v2/get_request_token" method="GET"/>
  6. <Access url="https://api.login.yahoo.com/oauth/v2/get_token" method="GET"/>
  7. <Authorization url="https://api.login.yahoo.com/oauth/v2/request_auth"/>
  8. <Signature method="HMAC-SHA1"/>
  9. </Service>
  10. </Application>
<?xml version="1.0"?>
<Application>
  <Service name="yahoo" type="OAuth">
    <Consumer key="..." secret="..."/>
    <Request url="https://api.login.yahoo.com/oauth/v2/get_request_token" method="GET"/>
    <Access url="https://api.login.yahoo.com/oauth/v2/get_token" method="GET"/>
    <Authorization url="https://api.login.yahoo.com/oauth/v2/request_auth"/>
    <Signature method="HMAC-SHA1"/>
  </Service>
</Application>
The Service tag can be repeated once for each external web service you'll be talking to. Its name attribute is used by your app to distinguish between different web services. There are several Service subtags, some of these are only required if your app will be making web service calls for the user's private data (three-legged OAuth):

Consumer
Mandatory subtag. Used to authenticate your app with the web service.
  • key: OAuth spec's consumer_key.
  • secret: OAuth spec's consumer_secret.

Request
Only required for private resources.
  • url: web service endpoint that gives an OAuth request token
  • method: whether OAuth parameters are sent via POST or GET

Access
Only required for private resources.
  • url: web service endpoint that gives an OAuth access token
  • method: whether OAuth parameters are sent via POST or GET

Authorization
  • url: OAuth standard's consumer authorization url.

Signature
  • method: How will the OAuth signature be encrypted: 'HMAC_SHA1', 'RSA-SHA1', 'PLAINTEXT'.

Calling a Webservice that Returns Public Data

This is the so-called two-legged OAuth case, where you're accessing a resource that isn't private user data, but still requires your app to be authorized for access. Suppose you have an OAuth consumer key and secret to such an OAuth resource that lives at "http://api.example.com/data". Here's what your auth.xml would look like:

  1. <?xml version="1.0"?>
  2. <Application>
  3. <Service name="example" type="OAuth">
  4. <Consumer key="exampleKey" secret="exampleSecret"/>
  5. <Signature method="HMAC-SHA1"/>
  6. </Service>
  7. </Application>
<?xml version="1.0"?>
<Application>
  <Service name="example" type="OAuth">
    <Consumer key="exampleKey" secret="exampleSecret"/>
    <Signature method="HMAC-SHA1"/>
  </Service>
</Application>
In your app, you would call the web service using the AuthService APIs:

  1. var auth = openmail.Application.getAuthService('example');
  2. var url = "http://api.example.com/data";
  3.  
  4. function onResponse(response){
  5. if(response.error) {
  6. //unexpected web service result
  7. . . .
  8. return;
  9. }
  10.  
  11. alert(response.data);
  12. }
  13.  
  14. //This imaginary web service takes one parameter, 'format'
  15. auth.callWebService( {url : url, method:'GET', parameters: {format:'text'}},
  16. onResponse);
var auth = openmail.Application.getAuthService('example');
var url = "http://api.example.com/data";
 
function onResponse(response){
    if(response.error) {
         //unexpected web service result
         . . .
         return;
    }
 
    alert(response.data);
}
 
//This imaginary web service takes one parameter, 'format'
auth.callWebService( {url : url, method:'GET', parameters: {format:'text'}},
                     onResponse);

Calling a Webservice that Returns Private Data

Suppose now that our example web service requires user authorization. Here's what the auth.xml might look like:

  1. <?xml version="1.0"?>
  2. <Application>
  3. <Service name="example" type="OAuth">
  4. <Consumer key="exampleKey" secret="exampleSecret"/>
  5. <Request url="https://api.login.example.com/request_token" method="GET"/>
  6. <Access url="https://api.login.example.com/access_token" method="GET"/>
  7. <Authorization url="https://api.login.example.com/authorization"/>
  8. <Signature method="HMAC-SHA1"/>
  9. </Service>
  10. </Application>
<?xml version="1.0"?>
<Application>
  <Service name="example" type="OAuth">
    <Consumer key="exampleKey" secret="exampleSecret"/>
    <Request url="https://api.login.example.com/request_token" method="GET"/>
    <Access url="https://api.login.example.com/access_token" method="GET"/>
    <Authorization url="https://api.login.example.com/authorization"/>
    <Signature method="HMAC-SHA1"/>
  </Service>
</Application>
The app cannot make calls to the webservice until the user has logged in and authorized access. Orchestrate user login, authorization, and logout (if desired) using the remaining AuthService APIs.

The way the login flow works, users must visit an external site specific to the webservice to login and authorize access to their data, then return to your app and take some action to notify the Auth Service API that they are ready to continue. (This is admittedly clunky, and we do support a refined process if you are calling a Yahoo! OAuth webservice.)

    Consider an app that allows the user to click different buttons to:

  • start the login and authorization flow
  • notify the app that authorization is complete
  • make an authenticated web service call
  • logout from the web service

Its view HTML might have some elements like this to set up the DOM for its buttons.

  1. <button id='login' onclick='handleLogin()' disabled>Login to Example.com</button><br/>
  2. <button id= 'complete' onclick='handleComplete()' disabled>Authorization Complete</button><br/>
  3. <button id= 'callWs' onclick='handleCall()' disabled>Call Webservice</button><br/>
  4. <button id= 'logout' onclick='handleLogout()' disabled>Logout from Example.com</button><br/>
<button id='login' onclick='handleLogin()' disabled>Login to Example.com</button><br/>
<button id= 'complete' onclick='handleComplete()' disabled>Authorization Complete</button><br/>
<button id= 'callWs' onclick='handleCall()' disabled>Call Webservice</button><br/>
<button id= 'logout' onclick='handleLogout()' disabled>Logout from Example.com</button><br/>

The accompanying javascript might look like this:

  1.  
  2. var auth = openmail.Application.getAuthService( { profile : 'example' });
  3.  
  4. function toggleButton(id)
  5. {
  6. var el = document.getElementById(id);
  7. el.disabled = !el.disabled;
  8. }
  9.  
  10. //AuthService callback for getStatus
  11. function onGotStatus(args) {
  12. if(args.data.status == 'ok') {
  13. toggleButton('callWs');
  14. toggleButton('logout');
  15. }
  16. else if (args.data.status == 'authorization required') {
  17. toggleButton('login');
  18. }
  19. else {
  20. //error handling
  21. . . .
  22. }
  23. }
  24.  
  25. //AuthService callback for getUserAuthorizationURL
  26. function onGotAuthURL(args) {
  27. window.open(args.data.url,'Login');
  28. toggleButton('login');
  29. toggleButton('authComplete');
  30. }
  31.  
  32. //login button handler
  33. function handleLogin() {
  34. auth.getUserAuthorizationURL({request:{},
  35. authorization:{permissions:'write'}},
  36. onGotAuthURL);
  37. }
  38.  
  39. //AuthService callback for notifyAuthorizationReceived
  40. function onNotifyAuthorizationReceived(args){
  41. if(args.error){
  42. //error case
  43. . . .
  44. return;
  45. }
  46.  
  47. toggleButton('authComplete');
  48. toggleButton('callWs');
  49. toggleButton('logout');
  50. }
  51.  
  52. //complete button handler
  53. function handleComplete() {
  54. auth.notifyAuthorizationReceived(onNotifyAuthorizationReceived);
  55. }
  56.  
  57. //AuthService callback for callWebService
  58. funcion onWebServiceResponse(args){
  59. if(args.error){
  60. //error case
  61. . . .
  62. }
  63. //do something with the data returned by the webservice
  64. alert(args.data);
  65. }
  66.  
  67. //call button handler
  68. function handleCall() {
  69. var url = "http://api.example.com/data";
  70. auth.callWebService({url:url, method:'GET', parameters:{format:'text'}},
  71. onWebServiceResponse); });
  72. }
  73.  
  74. //AuthService callback for removeAuthorization
  75. function onAuthorizationRemoved(args){
  76. toggleButton('callWs');
  77. toggleButton('logout');
  78. toggleButton('login');
  79. }
  80.  
  81. //logout button handler
  82. function handleLogout() {
  83. auth.removeAuthorization(onAuthorizationRemoved);
  84. }
  85.  
  86. auth.getStatus(onGotStatus);
  87.  
 
var auth = openmail.Application.getAuthService( { profile : 'example' });
 
function toggleButton(id)
{
    var el = document.getElementById(id);
    el.disabled = !el.disabled;
}
 
//AuthService callback for getStatus
function onGotStatus(args) {
    if(args.data.status == 'ok') {
       toggleButton('callWs');
       toggleButton('logout');
    }
    else if (args.data.status == 'authorization required') {
       toggleButton('login');
    }
    else {
        //error handling
        . . .
    }
}
 
//AuthService callback for getUserAuthorizationURL
function onGotAuthURL(args) {
    window.open(args.data.url,'Login');
    toggleButton('login');
    toggleButton('authComplete');
}
 
//login button handler
function handleLogin() {
  auth.getUserAuthorizationURL({request:{},
                                authorization:{permissions:'write'}},
                               onGotAuthURL);
}
 
//AuthService callback for notifyAuthorizationReceived
function onNotifyAuthorizationReceived(args){
    if(args.error){
         //error case
         . . .
         return;
    }
 
    toggleButton('authComplete');
    toggleButton('callWs');
    toggleButton('logout');
}
 
//complete button handler
function handleComplete() {
    auth.notifyAuthorizationReceived(onNotifyAuthorizationReceived);
}
 
//AuthService callback for callWebService
funcion onWebServiceResponse(args){
    if(args.error){
        //error case
        . . .
    }
    //do something with the data returned by the webservice
    alert(args.data);
}
 
//call button handler
function handleCall() {
    var url = "http://api.example.com/data";
    auth.callWebService({url:url, method:'GET', parameters:{format:'text'}},
                        onWebServiceResponse);  });
}
 
//AuthService callback for removeAuthorization
function onAuthorizationRemoved(args){
    toggleButton('callWs');
    toggleButton('logout');
    toggleButton('login');
}
 
//logout button handler
function handleLogout() {
    auth.removeAuthorization(onAuthorizationRemoved);
}
 
auth.getStatus(onGotStatus);
 

Obtaining a Key and Secret for Yahoo OAuth Endpoints

This is only necessary while you are developing your app without access to the AuthService's refined OAuth flow for Yahoo Endpoints.
  • Go to http://developer.yahoo.com/oauth.
  • Click on "Get an API Key".
  • Choose "New Web/Client App" from the popup.
  • Choose "Web Based" for Kind of Application.
  • Put in something useful for the "Application URL". For mail apps, we haven't defined a standard use for this field. It's used on the credential management page to identify your app.
  • Enter yom.mail.yahoo.com for the "Application Domain"
  • Choose the auth scopes you need.
  • You'll see a Domain Verification page for yom.mail.yahoo.com. To verify yom.mail.yahoo.com, you just need to click the button.
  • Put the new keys in your auth.xml.

Refined OAuth for Yahoo Endpoints

If the OAuth endpoint is a Yahoo! service, you can take advantage of a refined user authorization flow that avoids having the user log in a second time. When enabled, AuthService's streamlined flow automatically asks the user for authorization on application install or on the first authenticated call to a Y! service. Your application doesn't need to manage the process at all.

You cannot enable this feature yourself. A Yahoo! Mail Application Platform admin must do it. We will not enable this feature until we believe your app is a serious contender for publication in our app gallery, so you should initially develop with the default support.

Here's How It Works

  • Determine which services and scopes your application will require. Available auth scopes are listed at the bottom of https://developer.apps.yahoo.com/dashboard/createKey.html.
  • Figure out your appid using 'ymdt apps'.
  • Email us your appid and scope list (if you don't know the address for email support from us, you haven't graduated to a level where we're willing to give you access to this feature yet.) Only ask for the smallest set of scopes your application will require. Users will see all of the scopes your application is permitted to use on application install, and will likely wonder (for example) why your app requires Delicious access when you're only using their contact list.
  • We will generate a special OAuth key for your app, that you won't ever need to see or put in your auth.xml.

Make use of the passwordless scope like so:
  1. //Get special AuthService instance for access to Yahoo! Endpoint refined
  2. //authorization flow. This only works if a Yahoo! Mail Application admin has
  3. //configured your app's scopes server-side.
  4. var auth = openmail.Application.getAuthService({});
  5.  
  6. //Make webservice calls without orchestrating the auth flow. For example,
  7. //assuming we have Yahoo! Profiles in our scopes list, we could makes a query on
  8. //the YQL table "social.profile" to retrieve the current user's Y! profile info.
  9.  
  10. auth.callWebService({
  11. url: 'http://query.yahooapis.com/v1/yql',
  12. method: 'GET',
  13. parameters: {
  14. format: "json",
  15. q: 'select * from social.profile where guid=me'
  16. },
  17. function(args) {
  18. alert(["got data: ", args.data].join(''));
  19. }
  20. });
//Get special AuthService instance for access to Yahoo! Endpoint refined
//authorization flow.  This only works if a Yahoo! Mail Application admin has
//configured your app's scopes server-side.
var auth = openmail.Application.getAuthService({});
 
//Make webservice calls without orchestrating the auth flow.  For example,
//assuming we have Yahoo! Profiles in our scopes list, we could makes a query on
//the YQL table "social.profile" to retrieve the current user's Y! profile info.
 
auth.callWebService({
    url: 'http://query.yahooapis.com/v1/yql',
    method: 'GET',
    parameters: {
        format: "json",
        q: 'select * from social.profile where guid=me'
    },
    function(args) {
        alert(["got data: ", args.data].join(''));
    }
});

Flickr Auth and BBAuth

A handful of Yahoo! APIs use non-OAuth auth mechanisms. Flickr, for instance, uses the Flickr Authentication API. Another technology used at Yahoo is BBAuth. Both achieve the same thing OAuth provides. The Yahoo Developer Network provides a comparison between BBAuth and OAuth.

The Yahoo! Mail Development Platform supports both of these through minor variations of its OAuth support. For Flickr Auth, just set your auth.xml Service type attribute to "Flickr" and set your Consumer key and secret attributes to a Flickr API key and shared secret. For BBAuth, set the type attribute to "BBAuth" and the Consumer key an secret attributes to a BBAuth API key and shared secret. When creating the BBAuth application key/secret, request the redirect endpoint to be "http://yom.mail.yahoo.com/openmail/success.php".

Want a Working Example?

Try out the YQL sample app.

Support & Community

Ask questions and share insights on our forum.