Permalink
Browse files

table for verification of openif assertion

  • Loading branch information...
1 parent 14f1de5 commit 3c2e63bbb4004164be8378443b1d62c8bb866fb0 erik committed Apr 5, 2010
Showing with 168 additions and 0 deletions.
  1. +168 −0 openid/openid.verify.xml
View
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd" https="true">
+ <meta>
+ <author>Erik Eldridge w/ help from Yu Wang</author>
+ <description>
+ Verify positive assertion
+ </description>
+ <sampleQuery>select * from {table} where localUrl='http://example.com' and assertJson='{...}' and assocJson='{...}'</sampleQuery>
+ <documentationURL>http://wiki.github.com/ydn/yql-openid-support/</documentationURL>
+ </meta>
+ <bindings>
+ <select produces="JSON">
+ <inputs>
+ <key id="localUrl" type="xs:string" paramType="variable" required="true"/>
+ <key id="assertJson" type="xs:string" paramType="variable" required="true"/>
+ <key id="assocJson" type="xs:string" paramType="variable" required="true"/>
+ <key id="nonceStoreUri" type="xs:string" paramType="variable"/>
+ </inputs>
+ <execute><![CDATA[
+
+ //credit: http://javascript.crockford.com/remedial.html
+ if(!String.prototype.supplant){String.prototype.supplant=function(o){return this.replace(/{([^{}]*)}/g,function(a,b){var r=o[b];return typeof r==='string'||typeof r==='number'?r:a;});};}if(!String.prototype.trim){String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"");};}
+
+ y.include( 'store://pitrYOXYb8vQfiui4rUYPX' );
+ y.include('http://test.erikeldridge.com/yql/2.0.0-crypto-sha1.js');
+ y.include('http://crypto-js.googlecode.com/files/2.0.0-hmac-min.js');
+
+ response.object = function() {
+
+ try {
+ var assertion = JSON.parse(decodeURIComponent(assertJson));
+ } catch ( e ) {
+ return {
+ "error": "invalid assertJson: "+e
+ };
+ }
+
+ //note: this doesn't validate query parameters in return_to
+ if (assertion['openid.return_to'].trim() !== localUrl) {
+ return {
+ "error": "return_to url (" + assertion['openid.return_to'] + ") doesn't match local url (" + localUrl + ")"
+ };
+ }
+
+ //2) Verifying Discovered Information
+ //perform disco on the Claimed Id
+ //try yadis
+ var claimedId = assertion['openid.claimed_id'].split('#')[0],
+ githubAcct = 'erikeldridge',
+ yadisYql = 'use "{yadisTableUri}" as yadis; select * from yadis where uri = "{claimedId}"'.supplant({
+ yadisTableUri: 'http://github.com/' + githubAcct + '/yql-tables/raw/master/openid/openid.yadis.xml',
+ claimedId: claimedId
+ }),
+ yadisResults = y.xmlToJson(y.query(yadisYql).results);
+
+ if (yadisResults.results.result && 'success' === yadisResults.results.result.status) {
+
+ //if xrds, the protocol version, OP Endpoint URL &
+ //the OP-Local Identifier (if different than the Claimed Identifier)
+ //MUST be present in one <xrd:Service> element.
+ var xrdsQuery = 'select * from xml where url="{url}"'.supplant({
+ url: yadisResults.results.result.uri,
+ }),
+ xrdsResults = y.xmlToJson(y.query(xrdsQuery).results),
+ protocolVerified = false;
+
+ //check protocol
+ assertion['openid.ns'] = assertion['openid.ns'].trim();
+ for each(var type in xrdsResults.results.XRDS.XRD.Service.Type) {
+ if (-1 !== type.indexOf(assertion['openid.ns'])) {
+ protocolVerified = true;
+ break;
+ }
+ }
+
+ if (!protocolVerified) {
+ return {
+ "error": "Discovered protocol version doesn't match one in assertion"
+ };
+ }
+
+ //check if protocol and op endpoint are in the same service element
+ if (!xrdsResults.results.XRDS.XRD.Service.URI) {
+ return {
+ "error": "OP Endpoint URL MUST be present in one <xrd:Service>"
+ };
+ }
+
+ var opEndpointUri = xrdsResults.results.XRDS.XRD.Service.URI;
+ } else {
+
+ //try html
+ var htmlYql = 'select * from html where url="{claimedId}" and xpath=\'//link[@rel="openid2.provider"]\''.supplant({
+ claimedId: claimedId
+ }),
+ htmlResults = y.xmlToJson(y.query(htmlYql).results);
+ if (htmlResults.results) {
+ var opEndpointUri = htmlResults.results.link.href;
+ }
+ }
+
+ if (!opEndpointUri) {
+ return {
+ "error": "discovery failed; no op endpoint uri found"
+ };
+ } else if (opEndpointUri !== assertion['openid.op_endpoint']) {
+ return {
+ "error": "passed op endpoint != discovered op endpoint"
+ };
+ }
+
+ // 3) verify nonce (http://openid.net/specs/openid-authentication-2_0.html#verify_nonce)
+ // note: this expects a service w/ api: GET {uri}/{key}; POST {uri}/{key}, value='true'
+ if (nonceStoreUri) {
+ var key = y.crypto.encodeMd5(assertion.response_nonce);
+ var resp = y.rest(nonceStoreUri+'get/'+key).get().response;
+ if (resp) {
+ return {
+ "error": "invalid nonce"
+ };
+ } else {
+ resp = y.rest(nonceStoreUri+'put/'+key).contentType('application/x-www-form-urlencoded').query({
+ 'value': 'true'
+ }).post().response
+ }
+ }
+
+ // 4) verify signatures (http://openid.net/specs/openid-authentication-2_0.html#verifying_signatures)
+ var message = '';
+ for each(var key in assertion['openid.signed'].split(',')) {
+ message += key + ':' + assertion['openid.' + key] + '\n';
+ }
+
+ var assoc = JSON.parse(decodeURIComponent(assocJson));
+
+ if ('no-encryption' === assoc['session_type'] && 'HMAC-SHA1' === assoc['assoc_type']) {
+ raw_signature = Crypto.HMAC(
+ Crypto.SHA1, message, Crypto.util.base64ToBytes(assoc['mac_key']), {
+ asBytes: true
+ });
+ } else if ('no-encryption' === assoc['session_type'] && 'HMAC-SHA256' === assoc['assoc_type']) {
+ raw_signature = Crypto.HMAC(
+ Crypto.SHA256, message, Crypto.util.base64ToBytes(assoc['mac_key']), {
+ asBytes: true
+ });
+ } else {
+ return {
+ "error": "only no-encryption w/ sha1 or sha256 is supported at this time"
+ };
+ }
+
+ var sig = Crypto.util.bytesToBase64(raw_signature);
+
+ if (sig !== assertion['openid.sig']) {
+ return {
+ "error": "signatures do not match"
+ };
+ }
+
+ return {
+ "success": ":)"
+ };
+
+ }();
+ ]]></execute>
+ </select>
+ </bindings>
+</table>

0 comments on commit 3c2e63b

Please sign in to comment.