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.
On This Page
- Endpoint Flash Crossdomain Requirement
- Auth Service Configuration File
- Calling a Webservice that Returns Public Data
- Calling a Webservice that Returns Private Data
- Obtaining a Key and Secret for Yahoo OAuth Endpoints
- Refined Auth Flow for Yahoo OAuth Endpoints
- Flickr Auth and BBAuth
- Want a Working Example?
- Support & Community
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.
<?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>
<?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:
<?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>
<?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>
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:
<?xml version="1.0"?> <Application> <Service name="example" type="OAuth"> <Consumer key="exampleKey" secret="exampleSecret"/> <Signature method="HMAC-SHA1"/> </Service> </Application>
<?xml version="1.0"?> <Application> <Service name="example" type="OAuth"> <Consumer key="exampleKey" secret="exampleSecret"/> <Signature method="HMAC-SHA1"/> </Service> </Application>
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);
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:
<?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>
<?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 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.
<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/>
<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:
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);
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.
//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('')); } });
//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.