Skip to content
Merged
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
1 change: 1 addition & 0 deletions api-client/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './oauth2'
66 changes: 66 additions & 0 deletions api-client/src/auth/oauth2/getOAuth2Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { POST, request } from '../../request'

import type { ResponsePromise } from '../../request'
import type { HostConfig } from '../../types'

/** Our server currently expects us to supply this hard-coded client_id. */
export const OAUTH2_CLIENT_ID = 'opentrons_app' as const

/**
* An OAuth 2 "Resource owner password credentials" request,
* to exchange a username+password for an access token.
*
* https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
*/
export interface ROPCRequest {
grant_type: 'password'
username: string
password: string
// RFC 6749 seems to say client_id shouldn't be necessary here for public
// clients like the Opentrons App, but our server currently requires it anyway.
client_id: typeof OAUTH2_CLIENT_ID
}

/**
* A refresh request, to get a new access token before the old one expires.
*
* https://datatracker.ietf.org/doc/html/rfc6749#section-6
*/
export interface RefreshRequest {
grant_type: 'refresh_token'
refresh_token: string
// RFC 6749 seems to say client_id shouldn't be necessary here for public
// clients like the Opentrons App, but our server currently requires it anyway.
client_id: typeof OAUTH2_CLIENT_ID
}

export interface OAuth2TokenResponse {
/**
* In practice, token_type will always be "Bearer" for us,
* but calling code should validate it and refuse token_types
* that it doesn't understand.
*/
token_type: string
access_token: string
refresh_token?: string
expires_in?: number /** In seconds. */
scope?: string
}

/**
* Obtain an OAuth 2 access token.
*
* https://datatracker.ietf.org/doc/html/rfc6749#section-3.2
*/
export function getOAuth2Token(
config: HostConfig,
body: ROPCRequest | RefreshRequest
): ResponsePromise<OAuth2TokenResponse> {
const encodedBody = new URLSearchParams({ ...body })
return request<OAuth2TokenResponse, URLSearchParams>(
POST,
'/auth/oauth2/token',
encodedBody,
config
)
}
1 change: 1 addition & 0 deletions api-client/src/auth/oauth2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './getOAuth2Token'
1 change: 1 addition & 0 deletions api-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// api client entry point
export * from './auth'
export * from './calibration'
export * from './client_data'
export * from './camera'
Expand Down
1 change: 1 addition & 0 deletions react-api-client/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './oauth2'
1 change: 1 addition & 0 deletions react-api-client/src/auth/oauth2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useGetOAuth2TokenMutation'
55 changes: 55 additions & 0 deletions react-api-client/src/auth/oauth2/useGetOAuth2TokenMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useMutation } from 'react-query'

import { getOAuth2Token } from '@opentrons/api-client'

import { useHost } from '../../api'

import type {
UseMutateFunction,
UseMutationOptions,
UseMutationResult,
} from 'react-query'
import type {
HostConfig,
OAuth2TokenResponse,
RefreshRequest,
Response,
ROPCRequest,
} from '@opentrons/api-client'

export type GetOAuth2TokenMutationResult = UseMutationResult<
Response<OAuth2TokenResponse>,
unknown,
ROPCRequest | RefreshRequest
> & {
getOAuth2Token: UseMutateFunction<
Response<OAuth2TokenResponse>,
unknown,
ROPCRequest | RefreshRequest
>
}

export type GetOAuth2TokenMutationOptions = UseMutationOptions<
Response<OAuth2TokenResponse>,
unknown,
ROPCRequest | RefreshRequest
>

export function useGetOAuth2TokenMutation(
options: GetOAuth2TokenMutationOptions = {},
hostOverride?: HostConfig | null
): GetOAuth2TokenMutationResult {
const contextHost = useHost()
const host =
hostOverride != null ? { ...contextHost, ...hostOverride } : contextHost

const mutation = useMutation(
[host, 'auth/oauth2/token'],
body => getOAuth2Token(host!, body),
options
)
return {
...mutation,
getOAuth2Token: mutation.mutate,
}
}
1 change: 1 addition & 0 deletions react-api-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// react api client entry point
export * from './api'
export * from './auth'
export * from './calibration'
export * from './camera'
export * from './deck_configuration'
Expand Down
Loading