fixes #6
This commit is contained in:
Sean McArthur 2014-03-20 13:11:18 -07:00
Родитель 6debcdd0be
Коммит 5b2d155265
5 изменённых файлов: 85 добавлений и 55 удалений

Просмотреть файл

@ -5,9 +5,15 @@
### URL Structure
```
https://<server-url>/oauth/<api-endpoint>
https://<server-url>/v1/<api-endpoint>
```
Note that:
- All API access must be over HTTPS
- The URL embeds a version identifier "v1"; future revisions of this API may introduce new version numbers.
- The base URL of the server may be configured on a per-client basis
### Errors
Invalid requests will return 4XX responses. Internal failures will return 5XX. Both will include JSON responses describing the error.
@ -38,10 +44,10 @@ The currently-defined error responses are:
## API Endpoints
- [POST /oauth/authorization][authorization]
- [POST /oauth/token][token]
- [POST /v1/authorization][authorization]
- [POST /v1/token][token]
### POST /oauth/authorization
### POST /v1/authorization
This endpoint should be used by the fxa-content-server, requesting that
we supply a short-lived code (currently 15 minutes) that will be sent
@ -69,7 +75,7 @@ Example:
https://example.domain/path?foo=bar&code=asdfqwerty&state=zxcvasdf
```
### POST /oauth/token
### POST /v1/token
After having received a [code][], the client sends that code (most
likely a server-side request) to this endpoint, to receive a
@ -100,5 +106,5 @@ Example:
}
```
[authorization]: #post-oauthauthorization
[token]: #post-oauthtoken
[authorization]: #post-v1authorization
[token]: #post-v1token

Просмотреть файл

@ -8,6 +8,12 @@ const path = require('path');
const convict = require('convict');
const conf = convict({
api: {
version: {
doc: 'Number part of versioned endpoints - ex: /v1/token',
default: 1
}
},
browserid: {
issuer: {
doc: 'We only accept assertions from this issuer',

Просмотреть файл

@ -2,6 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const version = require('./config').get('api.version');
function v(url) {
return '/v' + version + url;
}
module.exports = [
{
method: 'GET',
@ -15,12 +21,12 @@ module.exports = [
},
{
method: 'POST',
path: '/oauth/authorization',
path: v('/authorization'),
config: require('./routes/authorization')
},
{
method: 'POST',
path: '/oauth/token',
path: v('/token'),
config: require('./routes/token')
}
];

Просмотреть файл

@ -101,7 +101,7 @@ function assertRequestParam(result, param) {
}
describe('/oauth', function() {
describe('/v1', function() {
before(function(done) {
Promise.all([
@ -130,8 +130,8 @@ describe('/oauth', function() {
it('is required', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
client_id: undefined
})
@ -142,8 +142,8 @@ describe('/oauth', function() {
it('succeeds if passed', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams()
}).then(function(res) {
assert.equal(res.statusCode, 302);
@ -155,8 +155,8 @@ describe('/oauth', function() {
describe('?assertion', function() {
it('is required', function(done) {
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
assertion: undefined
})
@ -167,8 +167,8 @@ describe('/oauth', function() {
it('succeeds if passed', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams()
}).then(function(res) {
assert.equal(res.statusCode, 302);
@ -177,8 +177,8 @@ describe('/oauth', function() {
it('errors correctly if invalid', function(done) {
mockAssertion().reply(400, '{"status":"failure"}');
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams()
}).then(function(res) {
assert.equal(res.result.code, 400);
@ -191,8 +191,8 @@ describe('/oauth', function() {
describe('?redirect_uri', function() {
it('is optional', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
redirect_uri: client.redirectUri
})
@ -203,8 +203,8 @@ describe('/oauth', function() {
it('must be same as registered redirect', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
redirect_uri: 'http://herp.derp'
})
@ -218,8 +218,8 @@ describe('/oauth', function() {
describe('?state', function() {
it('is required', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
state: undefined
})
@ -229,8 +229,8 @@ describe('/oauth', function() {
});
it('is returned', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
state: 'aa'
})
@ -244,8 +244,8 @@ describe('/oauth', function() {
describe('?scope', function() {
it('is optional', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
scope: undefined
})
@ -259,8 +259,8 @@ describe('/oauth', function() {
describe('with a whitelisted client', function() {
it('should redirect to the redirect_uri', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams()
}).then(function(res) {
assert.equal(res.statusCode, 302);
@ -281,15 +281,15 @@ describe('/oauth', function() {
describe('/token', function() {
it('disallows GET', function(done) {
Server.get('/oauth/token').then(function(res) {
Server.api.get('/token').then(function(res) {
assert.equal(res.statusCode, 404);
}).done(done, done);
});
describe('?client_id', function() {
it('is required', function(done) {
Server.post({
url: '/oauth/token',
Server.api.post({
url: '/token',
payload: {
client_secret: secret,
code: unique.code().toString('hex')
@ -302,8 +302,8 @@ describe('/oauth', function() {
describe('?client_secret', function() {
it('is required', function(done) {
Server.post({
url: '/oauth/token',
Server.api.post({
url: '/token',
payload: {
client_id: clientId,
code: unique.code().toString('hex')
@ -314,8 +314,8 @@ describe('/oauth', function() {
});
it('must match server-stored secret', function(done) {
Server.post({
url: '/oauth/token',
Server.api.post({
url: '/token',
payload: {
client_id: clientId,
client_secret: unique.secret().toString('hex'),
@ -330,8 +330,8 @@ describe('/oauth', function() {
describe('?code', function() {
it('is required', function(done) {
Server.post({
url: '/oauth/token',
Server.api.post({
url: '/token',
payload: {
client_id: clientId,
client_secret: secret
@ -342,8 +342,8 @@ describe('/oauth', function() {
});
it('must match an existing code', function(done) {
Server.post({
url: '/oauth/token',
Server.api.post({
url: '/token',
payload: {
client_id: clientId,
client_secret: secret,
@ -365,8 +365,8 @@ describe('/oauth', function() {
};
db.registerClient(client2).then(function() {
mockAssertion().reply(200, VERIFY_GOOD);
return Server.post({
url: '/oauth/authorization',
return Server.api.post({
url: '/authorization',
payload: authParams({
client_id: client2.id.toString('hex')
})
@ -374,8 +374,8 @@ describe('/oauth', function() {
return url.parse(res.headers.location, true).query.code;
});
}).then(function(code) {
return Server.post({
url: '/oauth/token',
return Server.api.post({
url: '/token',
payload: {
// client is trying to use client2's code
client_id: clientId,
@ -399,14 +399,14 @@ describe('/oauth', function() {
_done.apply(this, arguments);
}
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams()
}).then(function(res) {
return url.parse(res.headers.location, true).query.code;
}).delay(60).then(function(code) {
return Server.post({
url: '/oauth/token',
return Server.api.post({
url: '/token',
payload: {
client_id: clientId,
client_secret: secret,
@ -423,15 +423,15 @@ describe('/oauth', function() {
describe('response', function() {
it('should return a correct response', function(done) {
mockAssertion().reply(200, VERIFY_GOOD);
Server.post({
url: '/oauth/authorization',
Server.api.post({
url: '/authorization',
payload: authParams({
scope: 'foo bar bar'
})
}).then(function(res) {
assert.equal(res.statusCode, 302);
return Server.post({
url: '/oauth/token',
return Server.api.post({
url: '/token',
payload: {
client_id: clientId,
client_secret: secret,

Просмотреть файл

@ -4,6 +4,7 @@
const Promise = require('../../lib/promise');
const Server = require('../../lib/server');
const version = require('../../lib/config').get('api.version');
function request(options) {
var server = Server.create();
@ -30,3 +31,14 @@ exports.get = function get(options) {
options.method = 'GET';
return request(options);
};
var api = {};
Object.keys(exports).forEach(function(key) {
api[key] = function api(options) {
options = opts(options);
options.url = '/v' + version + options.url;
return exports[key](options);
};
});
exports.api = api;