-
Notifications
You must be signed in to change notification settings - Fork 70
fix(sdk-auth): to encode request body #1581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import type { | |
| AuthRequest, | ||
| UserAuthOptions, | ||
| } from 'types/sdk' | ||
| import { decode, encode } from 'qss' | ||
| import defaultsDeep from 'lodash.defaultsdeep' | ||
| import { getErrorByCode } from '@commercetools/sdk-middleware-http' | ||
| import * as constants from './constants' | ||
|
|
@@ -106,11 +107,28 @@ export default class SdkAuth { | |
| config.token || SdkAuth._encodeClientCredentials(credentials) | ||
| const authType = config.authType || constants.DEFAULT_AUTH_TYPE | ||
|
|
||
| let body = `grant_type=${grantType}` | ||
| if (grantType !== 'refresh_token') body += `&scope=${scope}` | ||
| if (disableRefreshToken === true) body += '&refresh_token=false' | ||
| const isNotRefreshTokenGrantType = grantType !== 'refresh_token' | ||
| const initialBody = encode({ | ||
| grant_type: grantType, | ||
| ...(disableRefreshToken && { refresh_token: false }), | ||
| ...(isNotRefreshTokenGrantType && { scope }), | ||
| }) | ||
|
|
||
| return { basicAuth, authType, uri, body: initialBody, headers } | ||
| } | ||
|
|
||
| static _appendToRequestBody( | ||
| request: AuthRequest, | ||
| toAppend: Object | ||
| ): AuthRequest { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We return the request not just the body here. That makes this easier to compose from my perspective.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this change a lot 👍 |
||
| const previousDecodedRequestBody = request.body ? decode(request.body) : {} | ||
| const nextEncodedRequestBody = encode({ | ||
| ...previousDecodedRequestBody, | ||
| ...toAppend, | ||
| }) | ||
| request.body = nextEncodedRequestBody | ||
|
|
||
| return { basicAuth, authType, uri, body, headers } | ||
| return request | ||
| } | ||
|
|
||
| _process(request: AuthRequest) { | ||
|
|
@@ -159,29 +177,25 @@ export default class SdkAuth { | |
| return SdkAuth._parseResponseJson(response).then((jsonResponse) => { | ||
| if (SdkAuth._isErrorResponse(response)) | ||
| throw SdkAuth._createResponseError(jsonResponse, uri, response.status) | ||
|
|
||
| return SdkAuth._enrichTokenResponse(jsonResponse) | ||
| }) | ||
| } | ||
|
|
||
| static _appendUserCredentialsToBody( | ||
| body: string, | ||
| request: AuthRequest, | ||
| username: string, | ||
| password: string | ||
| ): string { | ||
| ): AuthRequest { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too 👍 |
||
| if (!(username && password)) | ||
| throw new Error('Missing required user credentials (username, password)') | ||
|
|
||
| return [ | ||
| body, | ||
| `username=${encodeURIComponent(username)}`, | ||
| `password=${encodeURIComponent(password)}`, | ||
| ] | ||
| .filter(Boolean) | ||
| .join('&') | ||
| return SdkAuth._appendToRequestBody(request, { username, password }) | ||
| } | ||
|
|
||
| static _enrichUriWithProjectKey(uri: string, projectKey: ?string): string { | ||
| if (!projectKey) throw new Error('Missing required option (projectKey)') | ||
|
|
||
| return uri.replace('--projectKey--', projectKey) | ||
| } | ||
|
|
||
|
|
@@ -216,21 +230,26 @@ export default class SdkAuth { | |
|
|
||
| anonymousFlow(anonymousId: string = '', config: CustomAuthOptions = {}) { | ||
| const _config = this._getRequestConfig(config) | ||
| const request = SdkAuth._buildRequest( | ||
| let request = SdkAuth._buildRequest( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We build the request, then append to its body and get a new request. That's what I mean by composing. |
||
| _config, | ||
| SdkAuth._enrichUriWithProjectKey( | ||
| this.ANONYMOUS_FLOW_URI, | ||
| _config.projectKey | ||
| ) | ||
| ) | ||
|
|
||
| if (anonymousId) request.body += `&anonymous_id=${anonymousId}` | ||
| if (anonymousId) | ||
| request = SdkAuth._appendToRequestBody(request, { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we could technically leave
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, didn't want to change such things much in this PR. Generally the composition in this code can be improve. Ideally, the |
||
| anonymous_id: anonymousId, | ||
| }) | ||
|
|
||
| return this._process(request) | ||
| } | ||
|
|
||
| clientCredentialsFlow(config: CustomAuthOptions = {}) { | ||
| const _config = this._getRequestConfig(config) | ||
| const request = SdkAuth._buildRequest(_config, this.BASE_AUTH_FLOW_URI) | ||
|
|
||
| return this._process(request) | ||
| } | ||
|
|
||
|
|
@@ -240,13 +259,9 @@ export default class SdkAuth { | |
| url: string | ||
| ) { | ||
| const { username, password } = credentials || {} | ||
| const request = SdkAuth._buildRequest(config, url, 'password') | ||
| let request = SdkAuth._buildRequest(config, url, 'password') | ||
|
|
||
| request.body = SdkAuth._appendUserCredentialsToBody( | ||
| request.body, | ||
| username, | ||
| password | ||
| ) | ||
| request = SdkAuth._appendUserCredentialsToBody(request, username, password) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same with this one, but might just make things too nested and unreadable. |
||
|
|
||
| return this._process(request) | ||
| } | ||
|
|
@@ -269,29 +284,30 @@ export default class SdkAuth { | |
| config: CustomAuthOptions = {} | ||
| ) { | ||
| const _config = this._getRequestConfig(config) | ||
|
|
||
| return this._passwordFlow(credentials, _config, this.BASE_AUTH_FLOW_URI) | ||
| } | ||
|
|
||
| refreshTokenFlow(token: string, config: CustomAuthOptions = {}) { | ||
| if (!token) throw new Error('Missing required token value') | ||
| const _config = this._getRequestConfig(config) | ||
|
|
||
| const request = SdkAuth._buildRequest( | ||
| _config, | ||
| this.BASE_AUTH_FLOW_URI, | ||
| 'refresh_token' | ||
| const _config = this._getRequestConfig(config) | ||
| const request = SdkAuth._appendToRequestBody( | ||
| SdkAuth._buildRequest(_config, this.BASE_AUTH_FLOW_URI, 'refresh_token'), | ||
| { refresh_token: token } | ||
| ) | ||
| request.body += `&refresh_token=${encodeURIComponent(token)}` | ||
|
|
||
| return this._process(request) | ||
| } | ||
|
|
||
| introspectToken(token: string, config: CustomAuthOptions = {}) { | ||
| const _config = this._getRequestConfig(config) | ||
| if (!token) throw new Error('Missing required token value') | ||
|
|
||
| const request = SdkAuth._buildRequest(_config, this.INTROSPECT_URI) | ||
| request.body = `token=${encodeURIComponent(token)}` | ||
| const _config = this._getRequestConfig(config) | ||
| const request = SdkAuth._appendToRequestBody( | ||
| SdkAuth._buildRequest(_config, this.INTROSPECT_URI), | ||
| { token } | ||
| ) | ||
|
|
||
| return this._process(request) | ||
| } | ||
|
|
@@ -313,12 +329,12 @@ export default class SdkAuth { | |
| headers, | ||
| }) | ||
|
|
||
| const request = SdkAuth._buildRequest(_config, uri) | ||
| let request = SdkAuth._buildRequest(_config, uri) | ||
| request.body = body || '' // let user to build their own body | ||
|
|
||
| if (credentials) | ||
| request.body = SdkAuth._appendUserCredentialsToBody( | ||
| request.body, | ||
| request = SdkAuth._appendUserCredentialsToBody( | ||
| request, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, we keep this pattern throughout 👍
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The conditionals are a bit hard to improve without better patterns of compositions. I will adjust those which don't involve them. |
||
| credentials.username, | ||
| credentials.password | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import nock from 'nock' | ||
| import { encode } from 'qss' | ||
| import Auth from '../src/auth' | ||
| import config from './resources/sample-config' | ||
| import response from './resources/sample-response.json' | ||
|
|
@@ -11,12 +12,15 @@ describe('Client Password flow', () => { | |
|
|
||
| test('should authenticate with correct user credentials', async () => { | ||
| const scope = nock(config.host) | ||
| .post(`/oauth/token`, { | ||
| grant_type: 'password', | ||
| scope: `manage_project:${config.projectKey}`, | ||
| username: 'user123', | ||
| password: 'pass123', | ||
| }) | ||
| .post( | ||
| `/oauth/token`, | ||
| encode({ | ||
| grant_type: 'password', | ||
| scope: `manage_project:${config.projectKey}`, | ||
| username: 'user123', | ||
| password: 'pass123', | ||
| }) | ||
| ) | ||
| .reply(200, JSON.stringify(response)) | ||
|
|
||
| expect(scope.isDone()).toBe(false) | ||
|
|
@@ -40,9 +44,12 @@ describe('Client Password flow', () => { | |
| .post( | ||
| `/oauth/token`, | ||
| // expected body | ||
| `grant_type=password&scope=manage_project:${config.projectKey}` + | ||
| `&username=user%204%5El*aJ%40ETso%2B%2F%5CHdE1!x0u4q5` + // encoded | ||
| `&password=pass%204%5El*aJ%40ETso%2B%2F%5CHdE1!x0u4q5` // encoded | ||
| encode({ | ||
|
||
| grant_type: 'password', | ||
| scope: `manage_project:${config.projectKey}`, | ||
| username: userCredentials.username, | ||
| password: userCredentials.password, | ||
| }) | ||
| ) | ||
| .reply(200, JSON.stringify(response)) | ||
|
|
||
|
|
@@ -63,9 +70,13 @@ describe('Client Password flow', () => { | |
| const scope = nock(config.host) | ||
| .post( | ||
| `/oauth/token`, | ||
| // expected body | ||
| `grant_type=password&scope=manage_project:${config.projectKey}` + | ||
| `&refresh_token=false&username=user&password=pass` | ||
| encode({ | ||
| grant_type: 'password', | ||
| refresh_token: false, | ||
| scope: `manage_project:${config.projectKey}`, | ||
| username: 'user', | ||
| password: 'pass', | ||
| }) | ||
| ) | ||
| .reply(200, JSON.stringify(response)) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Building up the initial request encoding all params.