Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
169 lines (146 sloc) 6.1 KB
<?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>