Skip to content

Commit cf4dfb1

Browse files
feat!: add support for pat token to authenticate (#154)
1 parent 2ea3d2e commit cf4dfb1

14 files changed

Lines changed: 141 additions & 65 deletions

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ var dc = require('dc-management-sdk-js');
3333

3434
### Authentication
3535

36-
The content management API uses OAuth 2 to authenticate requests.
37-
When creating an API client you can provide your API key and secret
36+
The content management API uses either OAuth2 or Person Access Tokens (PAT) to authenticate requests.
37+
38+
When using OAuth 2 to create an API client you can provide your API key and secret
3839
and the client will handle creating authentication tokens.
3940

40-
For assistance creating API credentials and configuring permissions please contact [Amplience Support](https://support.amplience.com/).
41+
For assistance creating API credentials, PAT's and configuring permissions please contact [Amplience Support](https://support.amplience.com/).
4142

4243
```typescript
4344
const client = new DynamicContent({
@@ -55,6 +56,22 @@ var client = new dc.DynamicContent({
5556
});
5657
```
5758

59+
Create a client using a PAT
60+
61+
```typescript
62+
const client = new DynamicContent({
63+
patToken: process.env.PAT
64+
});
65+
```
66+
67+
OR
68+
69+
```javascript
70+
var client = new dc.DynamicContent({
71+
patToken: process.env.PAT
72+
});
73+
```
74+
5875
### Making requests
5976

6077
The most common top level resources (such as Hub and ContentItem) can be requested directly by calling the appropriate method on the client instance. This will return a native promise that will yield the resource or an error (e.g. if you do not have permission to access the resource).

src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export { DynamicContent } from './lib/DynamicContent';
2-
export { OAuth2Client } from './lib/oauth2/services/OAuth2Client';
3-
export { OAuth2ClientCredentials } from './lib/oauth2/models/OAuth2ClientCredentials';
2+
export { Oauth2AuthHeaderProvider } from './lib/oauth2/services/Oauth2AuthHeaderProvider';
3+
export { PatTokenAuthHeaderProvider } from './lib/auth/PatTokenAuthHeaderProvider';
4+
export { Oauth2AuthHeaderProviderCredentials } from './lib/oauth2/models/Oauth2AuthHeaderProviderCredentials';
45
export { AccessToken } from './lib/oauth2/models/AccessToken';
5-
export { AccessTokenProvider } from './lib/oauth2/models/AccessTokenProvider';
66
export { HalClient, DefaultHalClient } from './lib/hal/services/HalClient';
77
export {
88
HalResource,

src/lib/DynamicContent.mocks.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { HalClient } from './hal/services/HalClient';
44
import { HalLiteral, HalMocks } from './hal/utils/HalMock';
55
import { AxiosHttpClient } from './http/AxiosHttpClient';
66
import { HttpClient } from './http/HttpClient';
7-
import { AccessTokenProvider } from './oauth2/models/AccessTokenProvider';
8-
import { OAuth2ClientCredentials } from './oauth2/models/OAuth2ClientCredentials';
7+
import { Oauth2AuthHeaderProviderCredentials } from './oauth2/models/Oauth2AuthHeaderProviderCredentials';
98

109
/* tslint:disable:object-literal-sort-keys */
1110

@@ -2993,6 +2992,8 @@ export class DynamicContentFixtures {
29932992
*/
29942993
import MockAdapter from 'axios-mock-adapter';
29952994
import { Status } from './model/Status';
2995+
import { AuthorizationConfig } from './auth/AuthorizationConfig';
2996+
import { AuthHeaderProvider } from './auth/AuthHeaderProvider';
29962997

29972998
/**
29982999
* @hidden
@@ -3001,12 +3002,12 @@ export class MockDynamicContent extends DynamicContent {
30013002
public mock: MockAdapter;
30023003

30033004
constructor(
3004-
clientCredentials?: OAuth2ClientCredentials,
3005+
authCredentials?: AuthorizationConfig,
30053006
dcConfig?: DynamicContentConfig,
30063007
httpClient?: AxiosRequestConfig
30073008
) {
30083009
super(
3009-
clientCredentials || {
3010+
authCredentials || {
30103011
client_id: 'client_id',
30113012
client_secret: 'client_secret',
30123013
},
@@ -3018,23 +3019,18 @@ export class MockDynamicContent extends DynamicContent {
30183019
protected createTokenClient(
30193020
/* eslint-disable unused-imports/no-unused-vars-ts */
30203021
dcConfig: DynamicContentConfig,
3021-
clientCredentials: OAuth2ClientCredentials,
3022+
clientCredentials: Oauth2AuthHeaderProviderCredentials,
30223023
httpClient: HttpClient
3023-
): AccessTokenProvider {
3024+
): AuthHeaderProvider {
30243025
/* eslint-enable */
30253026
return {
3026-
getToken: () =>
3027-
Promise.resolve({
3028-
access_token: 'token',
3029-
expires_in: 60,
3030-
refresh_token: 'refresh',
3031-
}),
3027+
getAuthHeader: () => Promise.resolve('bearer token'),
30323028
};
30333029
}
30343030

30353031
protected createResourceClient(
30363032
dcConfig: DynamicContentConfig,
3037-
tokenProvider: AccessTokenProvider,
3033+
tokenProvider: AuthHeaderProvider,
30383034
httpClient: HttpClient
30393035
): HalClient {
30403036
const client = super.createResourceClient(

src/lib/DynamicContent.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import { Hub, HubsPage } from './model/Hub';
1313
import { Page } from './model/Page';
1414
import { Pageable } from './model/Pageable';
1515
import { Snapshot } from './model/Snapshot';
16-
import { AccessTokenProvider } from './oauth2/models/AccessTokenProvider';
17-
import { OAuth2ClientCredentials } from './oauth2/models/OAuth2ClientCredentials';
18-
import { OAuth2Client } from './oauth2/services/OAuth2Client';
16+
import { Oauth2AuthHeaderProviderCredentials } from './oauth2/models/Oauth2AuthHeaderProviderCredentials';
17+
import { Oauth2AuthHeaderProvider } from './oauth2/services/Oauth2AuthHeaderProvider';
1918
import { HierarchyParents } from './model/HierarchyParents';
2019
import { HierarchyChildren } from './model/HierarchyChildren';
2120
import { WorkflowState } from './model/WorkflowState';
2221
import { Extension } from './model/Extension';
22+
import { AuthorizationConfig } from './auth/AuthorizationConfig';
23+
import { PatTokenAuthHeaderProvider } from './auth/PatTokenAuthHeaderProvider';
24+
import { AuthHeaderProvider } from './auth/AuthHeaderProvider';
2325

2426
/**
2527
* Configuration settings for Dynamic Content API client. You can optionally
@@ -270,7 +272,7 @@ export class DynamicContent {
270272
* @param httpClient Optional request settings, can be used to provide proxy settings, add interceptors etc
271273
*/
272274
constructor(
273-
clientCredentials: Partial<OAuth2ClientCredentials>,
275+
authCredentials: Partial<AuthorizationConfig>,
274276
dcConfig?: DynamicContentConfig,
275277
httpClient?: AxiosRequestConfig | HttpClient
276278
) {
@@ -289,7 +291,7 @@ export class DynamicContent {
289291

290292
const tokenClient = this.createTokenClient(
291293
dcConfig,
292-
clientCredentials as OAuth2ClientCredentials,
294+
authCredentials as AuthorizationConfig,
293295
httpClientInstance
294296
);
295297

@@ -302,11 +304,15 @@ export class DynamicContent {
302304

303305
protected createTokenClient(
304306
dcConfig: DynamicContentConfig,
305-
clientCredentials: OAuth2ClientCredentials,
307+
authCredentials: AuthorizationConfig,
306308
httpClient: HttpClient
307-
): AccessTokenProvider {
308-
return new OAuth2Client(
309-
clientCredentials,
309+
): AuthHeaderProvider {
310+
if (authCredentials.patToken) {
311+
return new PatTokenAuthHeaderProvider(authCredentials.patToken);
312+
}
313+
314+
return new Oauth2AuthHeaderProvider(
315+
authCredentials as Oauth2AuthHeaderProviderCredentials,
310316
{
311317
authUrl: dcConfig.authUrl,
312318
},
@@ -316,7 +322,7 @@ export class DynamicContent {
316322

317323
protected createResourceClient(
318324
dcConfig: DynamicContentConfig,
319-
tokenProvider: AccessTokenProvider,
325+
tokenProvider: AuthHeaderProvider,
320326
httpClient: HttpClient
321327
): HalClient {
322328
return new DefaultHalClient(dcConfig.apiUrl, httpClient, tokenProvider);

src/lib/auth/AuthHeaderProvider.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* AuthHeaderProvider is used to provide a getAuthHeader method to provide and authorization header
3+
* Oauth2AuthHeaderProvider and PatTokenAuthHeaderProvider are two options for providing an AuthHeaderProvider
4+
*/
5+
6+
export interface AuthHeaderProvider {
7+
getAuthHeader(): Promise<string>;
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* AuthorizationConfig
3+
* @description Either Oauth2AuthHeaderProviderCredentials or a Personal Access Token
4+
*/
5+
6+
export interface AuthorizationConfig {
7+
patToken?: string;
8+
client_id?: string;
9+
client_secret?: string;
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import test from 'ava';
2+
import { PatTokenAuthHeaderProvider } from './PatTokenAuthHeaderProvider';
3+
4+
test('It should return an auth header for a Pat token', async (t) => {
5+
const client = new PatTokenAuthHeaderProvider('amp-pat-token');
6+
const header = await client.getAuthHeader();
7+
t.is(header, 'bearer amp-pat-token');
8+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { AuthHeaderProvider } from './AuthHeaderProvider';
2+
export class PatTokenAuthHeaderProvider implements AuthHeaderProvider {
3+
constructor(private readonly patToken: string) {}
4+
5+
public async getAuthHeader(): Promise<string> {
6+
return `bearer ${this.patToken}`;
7+
}
8+
}

src/lib/hal/models/HalResource.spec.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ import MockAdapter from 'axios-mock-adapter';
1414
* @hidden
1515
*/
1616
const tokenProvider = {
17-
getToken: () =>
18-
Promise.resolve({
19-
access_token: 'token',
20-
expires_in: 500,
21-
refresh_token: 'refresh',
22-
}),
17+
getAuthHeader: () => Promise.resolve('bearer token'),
2318
};
2419

2520
/**

src/lib/hal/services/HalClient.spec.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,7 @@ function createMockClient(): [HalClient, any] {
2626
* @hidden
2727
*/
2828
const tokenProvider = {
29-
getToken: () =>
30-
Promise.resolve({
31-
access_token: 'token',
32-
expires_in: 500,
33-
refresh_token: 'refresh',
34-
}),
29+
getAuthHeader: () => Promise.resolve('bearer token'),
3530
};
3631

3732
test('fetchResource should load and parse resource', async (t) => {
@@ -137,21 +132,15 @@ test('requests should include auth token', async (t) => {
137132
test('should ask for token from provider every request', async (t) => {
138133
const httpClient = new AxiosHttpClient({});
139134

140-
let tokenCount = 0;
141135
const client = new DefaultHalClient('', httpClient, {
142-
getToken: () =>
143-
Promise.resolve({
144-
access_token: 'token' + tokenCount++,
145-
expires_in: 500,
146-
refresh_token: 'refresh',
147-
}),
136+
getAuthHeader: () => Promise.resolve('bearer token'),
148137
});
149138

150139
const mock = new MockAdapter(httpClient.client);
151140
mock
152141
.onGet('/hubs/1', undefined, {
153142
Accept: 'application/json, text/plain, */*',
154-
Authorization: 'bearer token0',
143+
Authorization: 'bearer token',
155144
})
156145
.reply(200, {
157146
name: 'hub 1',

0 commit comments

Comments
 (0)