Skip to content
Closed
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ All notable changes to the ordercloud-javascript-sdk will be documented in this
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [Unreleased]
- Breaking: `customRoles` is a property of the auth options object on `Auth.Login`, `Auth.ElevatedLogin`, `Auth.ClientCredentials`, and `Auth.Anonymous`, instead of its own argument. Custom roles with no standard roles are sent as the scope string. That case previously sent the literal text `undefined`.

# [13.0.0] - 2026-09-25
- Bring SDK up to date with API v1.0.470. Breaking: `ApiClient.ClientSecret` and `SecurityProfile.IsExternalRoleGroup` are removed. Buyer product `PriceSchedule` is now `BuyerPriceSchedule`.
- Update dev dependencies, including TypeScript 5.9 and TypeDoc 0.28. TypeDoc 0.28 requires the missing-exports plugin to be listed explicitly.
Expand Down
15 changes: 15 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

The objective of this guide is to document the breaking changes and updates required to migrate from one major version to the next.

## version 13.x.x to version 14.x.x
* `customRoles` is no longer a positional argument on `Auth.Login`, `Auth.ElevatedLogin`, `Auth.ClientCredentials`, or `Auth.Anonymous`. Pass it on the request options object, along with `cancelToken`, `requestType`, and (for `Anonymous`) `anonuserid`.

Before:
```typescript
Auth.Login(username, password, clientID, scope, customRoles, requestOptions)
Auth.Anonymous(clientID, scope, customRoles, { anonuserid })
```

After:
```typescript
Auth.Login(username, password, clientID, scope, { ...requestOptions, customRoles })
Auth.Anonymous(clientID, scope, { anonuserid, customRoles })
```

## version 11.x.x to version 12.x.x
* The `URL` and `SharedKey` properties have been removed from the `MessageSender` model, and `DeliveryConfigID` is now required. Configuration must be provided via a `DeliveryConfig` (managed through the new `DeliveryConfigurations` service) using either a `MailchimpConfig` or `MessageSenderConfig` delivery target. See the [updated Message Senders KB article](https://ordercloud.io/knowledge-base/message-senders#configuration-options) for more details.

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ Auth.Login(username, password, clientID, scope)
Tokens.SetAccessToken(token)
})
.catch(err => console.log(err));

// Custom roles are optional and belong on the request options object.
// ElevatedLogin, ClientCredentials, and Anonymous use the same shape.
// Anonymous also accepts anonuserid on that object.
Auth.Login(username, password, clientID, scope, {
customRoles: ['InventoryAdmin'],
})
```

## 🔍 Filtering
Expand Down
134 changes: 51 additions & 83 deletions codegen/templates/api/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@ import paramsSerializer from '../utils/paramsSerializer'
import { RequiredDeep } from '../models/RequiredDeep'
import OrderCloudError from '../utils/OrderCloudError'

/**
* @ignore
* not part of public api, don't include in generated docs
*
* Standard roles, then a space, then custom roles. Omitted when both lists are empty.
*/
function resolveAuthScope(
scope?: ApiRole[],
customRoles?: string[]
): string | undefined {
if (scope && !Array.isArray(scope)) {
throw new Error('scope must be a string array')
}
if (customRoles != null && !Array.isArray(customRoles)) {
throw new Error('custom roles must be defined as a string array')
}

if (scope?.length && customRoles?.length) {
return `${scope.join(' ')} ${customRoles.join(' ')}`
}
if (scope?.length) {
return scope.join(' ')
}
if (customRoles?.length) {
return customRoles.join(' ')
}
return undefined
}

class Auth {
constructor() {
if (typeof axios === 'undefined') {
Expand All @@ -33,7 +62,7 @@ class Auth {
* @param password of the user logging in
* @param client_id of the application the user is logging into
* @param scope optional roles being requested, if omitted will return all assigned roles
* @param customRoles optional custom roles being requested - string array
* @param requestOptions.customRoles optional custom roles being requested - string array
* @param requestOptions.cancelToken Provide an [axios cancelToken](https://github.com/axios/axios#cancellation) that can be used to cancel the request.
* @param requestOptions.requestType Provide a value that can be used to identify the type of request. Useful for error logs.
*/
Expand All @@ -42,34 +71,19 @@ class Auth {
password: string,
clientID: string,
scope?: ApiRole[],
customRoles?: string[],
requestOptions: {
customRoles?: string[]
cancelToken?: CancelToken
requestType?: string
} = {}
): Promise<RequiredDeep<AccessToken>> {
if (scope && !Array.isArray(scope)) {
throw new Error('scope must be a string array')
}
if (customRoles != null && !Array.isArray(customRoles)) {
throw new Error('custom roles must be defined as a string array')
}

let _scope: string | undefined
if (scope?.length && !customRoles?.length) {
_scope = scope.join(' ')
} else if (!scope?.length && customRoles?.length) {
_scope += ` ${customRoles.join(' ')}`
} else if (scope?.length && customRoles?.length) {
_scope = `${scope.join(' ')} ${customRoles.join(' ')}`
}

const { customRoles, ...axiosOptions } = requestOptions
const body = {
grant_type: 'password',
username,
password,
client_id: clientID,
scope: _scope,
scope: resolveAuthScope(scope, customRoles),
}
const configuration = Configuration.Get()
const response = await axios
Expand All @@ -82,7 +96,7 @@ class Auth {
Accept: 'application/json',
},
adapter: configuration.axiosAdapter,
...requestOptions,
...axiosOptions,
}
)
.catch(e => {
Expand All @@ -102,7 +116,7 @@ class Auth {
* @param password of the user logging in
* @param clientID of the application the user is logging into
* @param scope roles being requested - space delimited string or array
* @param customRoles optional custom roles being requested - string array
* @param requestOptions.customRoles optional custom roles being requested - string array
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param requestOptions.cancelToken Provide an [axios cancelToken](https://github.com/axios/axios#cancellation) that can be used to cancel the request.
Expand All @@ -114,31 +128,16 @@ class Auth {
password: string,
clientID: string,
scope?: ApiRole[],
customRoles?: string[],
requestOptions: {
customRoles?: string[]
cancelToken?: CancelToken
requestType?: string
} = {}
): Promise<RequiredDeep<AccessToken>> {
if (scope && !Array.isArray(scope)) {
throw new Error('scope must be a string array')
}
if (customRoles != null && !Array.isArray(customRoles)) {
throw new Error('custom roles must be defined as a string array')
}

let _scope: string | undefined
if (scope?.length && !customRoles?.length) {
_scope = scope.join(' ')
} else if (!scope?.length && customRoles?.length) {
_scope += ` ${customRoles.join(' ')}`
} else if (scope?.length && customRoles?.length) {
_scope = `${scope.join(' ')} ${customRoles.join(' ')}`
}

const { customRoles, ...axiosOptions } = requestOptions
const body = {
grant_type: 'password',
scope: _scope,
scope: resolveAuthScope(scope, customRoles),
client_id: clientID,
username,
password,
Expand All @@ -155,7 +154,7 @@ class Auth {
Accept: 'application/json',
},
adapter: configuration.axiosAdapter,
...requestOptions,
...axiosOptions,
}
)
.catch(e => {
Expand All @@ -173,7 +172,7 @@ class Auth {
* @param clientSecret of the application
* @param clientID of the application the user is logging into
* @param scope roles being requested - space delimited string or array
* @param customRoles optional custom roles being requested - string array
* @param requestOptions.customRoles optional custom roles being requested - string array
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param requestOptions.cancelToken Provide an [axios cancelToken](https://github.com/axios/axios#cancellation) that can be used to cancel the request.
Expand All @@ -183,31 +182,16 @@ class Auth {
clientSecret: string,
clientID: string,
scope?: ApiRole[],
customRoles?: string[],
requestOptions: {
customRoles?: string[]
cancelToken?: CancelToken
requestType?: string
} = {}
): Promise<RequiredDeep<AccessToken>> {
if (scope && !Array.isArray(scope)) {
throw new Error('scope must be a string array')
}
if (customRoles != null && !Array.isArray(customRoles)) {
throw new Error('custom roles must be defined as a string array')
}

let _scope: string | undefined
if (scope?.length && !customRoles?.length) {
_scope = scope.join(' ')
} else if (!scope?.length && customRoles?.length) {
_scope += ` ${customRoles.join(' ')}`
} else if (scope?.length && customRoles?.length) {
_scope = `${scope.join(' ')} ${customRoles.join(' ')}`
}

const { customRoles, ...axiosOptions } = requestOptions
const body = {
grant_type: 'client_credentials',
scope: _scope,
scope: resolveAuthScope(scope, customRoles),
client_id: clientID,
client_secret: clientSecret,
}
Expand All @@ -222,7 +206,7 @@ class Auth {
Accept: 'application/json',
},
adapter: configuration.axiosAdapter,
...requestOptions,
...axiosOptions,
}
)
.catch(e => {
Expand Down Expand Up @@ -283,45 +267,29 @@ class Auth {
*
* @param clientID of the application the user is logging into
* @param scope roles being requested - space delimited string or array
* @param customRoles optional custom roles being requested - string array
* @param requestOptions.customRoles optional custom roles being requested - string array
* @param requestOptions.anonuserid Provide an externally generated id to track this user session, used specifically for the tracking events feature for integrating with Send and Discover
* @param requestOptions.cancelToken Provide an [axios cancelToken](https://github.com/axios/axios#cancellation) that can be used to cancel the request.
* @param requestOptions.requestType Provide a value that can be used to identify the type of request. Useful for error logs.
*/
public async Anonymous(
clientID: string,
scope?: ApiRole[],
customRoles?: string[],
requestOptions: {
customRoles?: string[]
anonuserid?: string
cancelToken?: CancelToken
requestType?: string
} = {}
): Promise<RequiredDeep<AccessToken>> {
if (scope && !Array.isArray(scope)) {
throw new Error('scope must be a string array')
}
if (customRoles != null && !Array.isArray(customRoles)) {
throw new Error('custom roles must be defined as a string array')
}

let _scope: string | undefined
if (scope?.length && !customRoles?.length) {
_scope = scope.join(' ')
} else if (!scope?.length && customRoles?.length) {
_scope += ` ${customRoles.join(' ')}`
} else if (scope?.length && customRoles?.length) {
_scope = `${scope.join(' ')} ${customRoles.join(' ')}`
}

const { customRoles, anonuserid, ...axiosOptions } = requestOptions
const body = {
grant_type: 'client_credentials',
client_id: clientID,
scope: _scope,
scope: resolveAuthScope(scope, customRoles),
}
if (requestOptions.anonuserid) {
body['anonuserid'] = requestOptions.anonuserid
delete requestOptions['anonuserid']
if (anonuserid) {
body['anonuserid'] = anonuserid
}
const configuration = Configuration.Get()
const response = await axios
Expand All @@ -334,7 +302,7 @@ class Auth {
Accept: 'application/json',
},
adapter: configuration.axiosAdapter,
...requestOptions,
...axiosOptions,
}
)
.catch(e => {
Expand Down
Loading
Loading