Skip to content

Commit 30a9f0b

Browse files
hisabimbolawizzy25
authored andcommitted
feat(sdk-middleware-auth): implement anonymous session flow
affects: @commercetools/sdk-middleware-auth resolves #276
1 parent 55a504c commit 30a9f0b

File tree

11 files changed

+343
-82
lines changed

11 files changed

+343
-82
lines changed

docs/sdk/api/sdkMiddlewareAuth.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ const client = createClient({
4040
credentials: {
4141
clientId: '123',
4242
clientSecret: 'secret',
43+
user: {
44+
username: string;
45+
password: string;
46+
}
4347
},
4448
scopes: [
4549
'view_products:test',
@@ -58,6 +62,7 @@ Creates a [middleware](/sdk/Glossary.md#middleware) to handle authentication for
5862

5963
1. `host` *(String)*: the host of the OAuth API service
6064
2. `projectKey` *(String)*: the key of the project to assign the default scope to
65+
- The user field is an object containining `username` and `password`. [Sample below](#usage-example)
6166
3. `credentials` *(Object)*: the client credentials for authentication (`clientId`, `clientSecret`)
6267
4. `scopes` *(Array)*: a list of [scopes](http://dev.commercetools.com/http-api-authorization.html#scopes) to assign to the OAuth token. _No default scope is sent_
6368

@@ -89,3 +94,40 @@ const client = createClient({
8994
],
9095
})
9196
```
97+
98+
## `createAuthMiddlewareForAnonymousSessionFlow(options)`
99+
100+
Creates a [middleware](/sdk/Glossary.md#middleware) to handle authentication for the [Anonymous Session Flow](http://dev.commercetools.com/http-api-authorization.html#tokens-for-anonymous-sessions) of the commercetools platform API.
101+
102+
#### Named arguments (options)
103+
104+
1. `host` *(String)*: the host of the OAuth API service
105+
2. `projectKey` *(String)*: the key of the project to assign the default scope to
106+
3. `credentials` *(Object)*: the client credentials for authentication (`clientId`, `clientSecret`, `anonymousId`)
107+
4. `scopes` *(Array)*: a list of [scopes](http://dev.commercetools.com/http-api-authorization.html#scopes) (default `manage_project:{projectKey}`) to assign to the OAuth token
108+
109+
110+
#### Usage example
111+
112+
```js
113+
import { createClient } from '@commercetools/sdk-client'
114+
import { createAuthMiddlewareForAnonymousSessionFlow } from '@commercetools/sdk-middleware-auth'
115+
116+
const client = createClient({
117+
middlewares: [
118+
createAuthMiddlewareForAnonymousSessionFlow({
119+
host: 'https://auth.commercetools.com',
120+
projectKey: 'test',
121+
credentials: {
122+
clientId: '123',
123+
clientSecret: 'secret',
124+
anonymousId: 'unique-id-of-customer-not-required',
125+
},
126+
scopes: [
127+
'view_products:test',
128+
'manage_orders:test',
129+
],
130+
}),
131+
],
132+
})
133+
```

integration-tests/sdk/auth.it.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { createClient } from '@commercetools/sdk-client'
2+
import { getCredentials } from '@commercetools/get-credentials'
3+
import {
4+
createAuthMiddlewareForPasswordFlow,
5+
createAuthMiddlewareForAnonymousSessionFlow,
6+
} from '@commercetools/sdk-middleware-auth'
7+
import {
8+
createHttpMiddleware,
9+
} from '@commercetools/sdk-middleware-http'
10+
import { clearData, createData } from './../cli/helpers/utils'
11+
12+
let projectKey
13+
if (process.env.CI === 'true')
14+
projectKey = 'auth-integration-test'
15+
else
16+
projectKey = process.env.npm_config_projectkey
17+
18+
describe('Auth Flows', () => {
19+
let apiConfig
20+
const userEmail = `abi${Date.now()}@commercetooler.com`
21+
const userPassword = 'asdifficultaspossible'
22+
beforeAll(() => getCredentials(projectKey)
23+
.then((credentials) => {
24+
apiConfig = {
25+
host: 'https://auth.sphere.io',
26+
apiUrl: 'https://api.sphere.io',
27+
projectKey,
28+
credentials: {
29+
clientId: credentials.clientId,
30+
clientSecret: credentials.clientSecret,
31+
},
32+
}
33+
})
34+
.then(() => clearData(apiConfig, 'customers'))
35+
.then(() => clearData(apiConfig, 'carts'))
36+
.then(() => createData(apiConfig, 'customers', [{
37+
email: userEmail,
38+
password: userPassword,
39+
}]))
40+
, 10000)
41+
afterAll(() => {
42+
clearData(apiConfig, 'customers')
43+
.then(() => clearData(apiConfig, 'carts'))
44+
})
45+
describe('Password Session Flow', () => {
46+
const httpMiddleware = createHttpMiddleware({
47+
host: 'https://api.sphere.io',
48+
})
49+
50+
it('should log customer and fetch customer profile', () => {
51+
const userConfig = {
52+
...apiConfig,
53+
...{ scopes: [ `manage_project:${projectKey}` ] },
54+
...{ credentials: {
55+
clientId: apiConfig.credentials.clientId,
56+
clientSecret: apiConfig.credentials.clientSecret,
57+
user: {
58+
username: userEmail,
59+
password: userPassword,
60+
},
61+
} },
62+
}
63+
const client = createClient({
64+
middlewares: [
65+
createAuthMiddlewareForPasswordFlow(userConfig),
66+
httpMiddleware,
67+
],
68+
})
69+
return client.execute({
70+
uri: `/${projectKey}/me`,
71+
method: 'GET',
72+
}).then((response) => {
73+
const user = response.body
74+
expect(user).toHaveProperty('email', userEmail)
75+
})
76+
})
77+
})
78+
79+
describe('Anonymous Session Flow', () => {
80+
const httpMiddleware = createHttpMiddleware({
81+
host: 'https://api.sphere.io',
82+
})
83+
84+
it('create an anonymous session and a cart tied to the session', () => {
85+
const anonymousId = `${Date.now()}-bar`
86+
const userConfig = {
87+
...apiConfig,
88+
...{ scopes: [ `manage_project:${projectKey}` ] },
89+
...{ credentials: {
90+
clientId: apiConfig.credentials.clientId,
91+
clientSecret: apiConfig.credentials.clientSecret,
92+
anonymousId,
93+
} },
94+
}
95+
const client = createClient({
96+
middlewares: [
97+
createAuthMiddlewareForAnonymousSessionFlow(userConfig),
98+
httpMiddleware,
99+
],
100+
})
101+
const cartMock = {
102+
currency: 'EUR',
103+
}
104+
return client.execute({
105+
// creates a cart that is tied to the logged in anonymous token
106+
uri: `/${projectKey}/me/carts`,
107+
method: 'POST',
108+
body: cartMock,
109+
}).then(({ body: cart }) => {
110+
expect(cart).toHaveProperty('anonymousId', anonymousId)
111+
return client.execute({
112+
// fetch all carts tied to the anonymous token, if cart is present,
113+
// then the cart was tied to the token
114+
uri: `/${projectKey}/me/carts`,
115+
method: 'GET',
116+
})
117+
}).then(({ body: { results: carts } }) => {
118+
expect(carts).toHaveLength(1)
119+
expect(carts[0]).toHaveProperty('anonymousId', anonymousId)
120+
})
121+
}, 7000)
122+
})
123+
})

integration-tests/sdk/customer-login.it.js

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1-
export default function createAuthMiddlewareForAnonymousSessionFlow () {
2-
throw new Error('Middleware not implemented yet')
1+
/* @flow */
2+
import type {
3+
AuthMiddlewareOptions,
4+
Middleware,
5+
MiddlewareRequest,
6+
MiddlewareResponse,
7+
Next,
8+
Task,
9+
} from 'types/sdk'
10+
11+
import { buildRequestForAnonymousSessionFlow } from './build-requests'
12+
import authMiddlewareBase from './base-auth-flow'
13+
import store from './utils'
14+
15+
export default function createAuthMiddlewareForAnonymousSessionFlow (
16+
options: AuthMiddlewareOptions,
17+
): Middleware {
18+
const tokenCache = store({})
19+
const pendingTasks: Array<Task> = []
20+
21+
const requestState = store(false)
22+
return (next: Next) => (
23+
request: MiddlewareRequest,
24+
response: MiddlewareResponse,
25+
) => {
26+
const params = {
27+
request,
28+
response,
29+
...buildRequestForAnonymousSessionFlow(options),
30+
pendingTasks,
31+
requestState,
32+
tokenCache,
33+
}
34+
authMiddlewareBase(params, next)
35+
}
336
}

packages/sdk-middleware-auth/src/base-auth-flow.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import type {
55
Task,
66
AuthMiddlewareBaseOptions,
77
} from 'types/sdk'
8-
98
/* global fetch */
109
import 'isomorphic-fetch'
1110

12-
1311
export default function authMiddlewareBase ({
1412
request,
1513
response,

packages/sdk-middleware-auth/src/build-requests.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ export function buildRequestForPasswordFlow (
7878
const scope = (options.scopes || []).join(' ')
7979
const scopeStr = scope ? `&scope=${scope}` : ''
8080

81-
8281
const basicAuth = new Buffer(`${clientId}:${clientSecret}`).toString('base64')
8382
// This is mostly useful for internal testing purposes to be able to check
8483
// other oauth endpoints.
@@ -94,6 +93,21 @@ export function buildRequestForRefreshTokenFlow () {
9493
// TODO
9594
}
9695

97-
export function buildRequestForAnonymousSessionFlow () {
98-
// TODO
96+
export function buildRequestForAnonymousSessionFlow (
97+
options: AuthMiddlewareOptions,
98+
): BuiltRequestParams {
99+
if (!options)
100+
throw new Error('Missing required options')
101+
102+
if (!options.projectKey)
103+
throw new Error('Missing required option (projectKey)')
104+
const pKey = options.projectKey
105+
// eslint-disable-next-line no-param-reassign
106+
options.oauthUri = options.oauthUri || `/oauth/${pKey}/anonymous/token`
107+
const result = buildRequestForClientCredentialsFlow(options)
108+
109+
if (options.credentials.anonymousId)
110+
result.body += `&anonymous_id=${options.credentials.anonymousId}`
111+
112+
return { ...result }
99113
}

0 commit comments

Comments
 (0)