Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/resource-specific-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,36 @@ Contents of `database.json`
}
```

## Resource Servers

Resource servers (APIs) configuration supports the Management API payload schema. The following fields are supported:

**YAML Example**

```yaml
resourceServers:
- name: My API
identifier: https://api.example.com
proof_of_possession:
mechanism: dpop
required: true
required_for: public_clients
```

**Directory Example**

```json
{
"name": "My API",
"identifier": "https://api.example.com",
"proof_of_possession": {
"mechanism": "mtls",
"required": true,
"required_for": "all_clients"
}
}
```

## Universal Login

### Pages
Expand Down
4 changes: 4 additions & 0 deletions src/tools/auth0/handlers/resourceServers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export const schema = {
enum: Object.values(Management.ResourceServerProofOfPossessionMechanismEnum),
},
required: { type: 'boolean' },
required_for: {
type: 'string',
enum: Object.values(Management.ResourceServerProofOfPossessionRequiredForEnum),
},
},
required: ['mechanism', 'required'],
},
Expand Down
126 changes: 126 additions & 0 deletions test/tools/auth0/handlers/resourceServers.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,88 @@ describe('#resourceServers handler', () => {
]);
});

it('should create resource server with proof_of_possession and required_for(public_clients)', async () => {
const auth0 = {
resourceServers: {
create: function (data) {
(() => expect(this).to.not.be.undefined)();
expect(data).to.be.an('object');
expect(data.name).to.equal('dpopAPIWithRequiredFor');
expect(data.proof_of_possession).to.deep.equal({
mechanism: 'dpop',
required: true,
required_for: 'public_clients',
});
return Promise.resolve(data);
},
update: () => Promise.resolve([]),
delete: () => Promise.resolve([]),
list: (params) => mockPagedData(params, 'resource_servers', []),
},
pool,
};

const handler = new resourceServers.default({ client: pageClient(auth0), config });
const stageFn = Object.getPrototypeOf(handler).processChanges;

await stageFn.apply(handler, [
{
resourceServers: [
{
name: 'dpopAPIWithRequiredFor',
identifier: 'https://dpop-api-required-for.example.com',
proof_of_possession: {
mechanism: 'dpop',
required: true,
required_for: 'public_clients',
},
},
],
},
]);
});

it('should create resource server with proof_of_possession and required_for(all_clients)', async () => {
const auth0 = {
resourceServers: {
create: function (data) {
(() => expect(this).to.not.be.undefined)();
expect(data).to.be.an('object');
expect(data.name).to.equal('mtlsAPIWithRequiredFor');
expect(data.proof_of_possession).to.deep.equal({
mechanism: 'mtls',
required: true,
required_for: 'all_clients',
});
return Promise.resolve(data);
},
update: () => Promise.resolve([]),
delete: () => Promise.resolve([]),
list: (params) => mockPagedData(params, 'resource_servers', []),
},
pool,
};

const handler = new resourceServers.default({ client: pageClient(auth0), config });
const stageFn = Object.getPrototypeOf(handler).processChanges;

await stageFn.apply(handler, [
{
resourceServers: [
{
name: 'mtlsAPIWithRequiredFor',
identifier: 'https://mtls-api-required-for.example.com',
proof_of_possession: {
mechanism: 'mtls',
required: true,
required_for: 'all_clients',
},
},
],
},
]);
});

it('should create resource server with subject_type_authorization', async () => {
const auth0 = {
resourceServers: {
Expand Down Expand Up @@ -355,6 +437,50 @@ describe('#resourceServers handler', () => {
]);
});

it('should update resource server with proof_of_possession and required_for', async () => {
const auth0 = {
resourceServers: {
create: () => Promise.resolve([]),
update: function (id, data) {
expect(id).to.be.a('string');
expect(data).to.be.an('object');
expect(id).to.equal('rs1');
expect(data.proof_of_possession).to.deep.equal({
mechanism: 'dpop',
required: true,
required_for: 'public_clients',
});
return Promise.resolve(data);
},
delete: () => Promise.resolve([]),
list: (params) =>
mockPagedData(params, 'resource_servers', [
{ id: 'rs1', identifier: 'some-api', name: 'someAPI' },
]),
},
pool,
};

const handler = new resourceServers.default({ client: pageClient(auth0), config });
const stageFn = Object.getPrototypeOf(handler).processChanges;

await stageFn.apply(handler, [
{
resourceServers: [
{
name: 'someAPI',
identifier: 'some-api',
proof_of_possession: {
mechanism: 'dpop',
required: true,
required_for: 'public_clients',
},
},
],
},
]);
});

it('should update new resource server with same name but different identifier', async () => {
const auth0 = {
resourceServers: {
Expand Down
47 changes: 47 additions & 0 deletions test/tools/auth0/validator.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,53 @@ describe('#schema validation tests', () => {
done();
});
});

it('should pass validation with valid proof_of_possession including required_for', (done) => {
const data = [
{
name: 'name',
identifier: 'identifier',
proof_of_possession: {
mechanism: 'dpop',
required: true,
required_for: 'public_clients',
},
},
];

checkPassed({ resourceServers: data }, done);
});

it('should fail validation if proof_of_possession has invalid required_for', (done) => {
const data = [
{
name: 'name',
identifier: 'identifier',
proof_of_possession: {
mechanism: 'dpop',
required: true,
required_for: 'invalid_value',
},
},
];

const auth0 = new Auth0(
{
...client,
resourceServers: {
getAll: async (params) => mockPagedData(params, 'resource_servers', []),
},
},
{ resourceServers: data },
mockConfigFn
);

auth0.validate().then(failedCb(done), (err) => {
expect(err.message).to.contain('enum');
expect(err.message).to.contain('required_for');
done();
});
});
});

describe('#tenant validate', () => {
Expand Down