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
5 changes: 5 additions & 0 deletions .changeset/age-2331-remote-session-client-audience.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"server": minor
---

Add a nullable `audience` column to `remote_session_clients` and surface it on the remoteSessionClients management API. When set, the upstream OAuth dance attaches the `audience` parameter to the authorize redirect, the authorization-code → token exchange, and every refresh-token request; when unset the parameter is omitted entirely.
5 changes: 5 additions & 0 deletions .changeset/age-2331-remote-session-client-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"server": minor
---

Add a nullable `scope` column to `remote_session_clients` and surface it on the remoteSessionClients management API. When set, the upstream OAuth dance requests these scopes instead of echoing the issuer's full `scopes_supported`, which avoids over-granting Gram access on providers that advertise broad scope sets.
44 changes: 44 additions & 0 deletions .speakeasy/out.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27748,6 +27748,11 @@ components:
CloneClientFromOAuthProxyProviderForm:
type: object
properties:
audience:
type: string
description: Optional upstream OAuth audience to send on the authorize redirect and token exchange for the cloned client.
pattern: ^[!-~]+$
maxLength: 512
oauth_proxy_provider_id:
type: string
description: The oauth_proxy_provider to read client_id / client_secret from. Must live in the caller's project.
Expand All @@ -27756,6 +27761,13 @@ components:
type: string
description: The remote_session_issuer the new client is registered with.
format: uuid
scope:
type: array
items:
type: string
pattern: ^[!#-[\]-~]+$
maxLength: 128
description: Explicit upstream OAuth scopes the dance should request for the cloned client. Omit to fall back to the issuer's scopes_supported.
token_endpoint_auth_method:
type: string
description: How the cloned client authenticates at the issuer's token endpoint. Omit to default to client_secret_basic.
Expand Down Expand Up @@ -28265,6 +28277,11 @@ components:
CreateRemoteSessionClientForm:
type: object
properties:
audience:
type: string
description: Optional upstream OAuth audience to send on the authorize redirect and token exchange.
pattern: ^[!-~]+$
maxLength: 512
client_id:
type: string
description: client_id supplied by the caller.
Expand All @@ -28275,6 +28292,13 @@ components:
type: string
description: The owning remote_session_issuer id.
format: uuid
scope:
type: array
items:
type: string
pattern: ^[!#-[\]-~]+$
maxLength: 128
description: Explicit upstream OAuth scopes the dance should request for this client. Omit to fall back to the issuer's scopes_supported.
token_endpoint_auth_method:
type: string
description: How the client authenticates at the issuer's token endpoint. Omit to default to client_secret_basic.
Expand Down Expand Up @@ -33764,6 +33788,9 @@ components:
RemoteSessionClient:
type: object
properties:
audience:
type: string
description: Upstream OAuth audience sent on the authorize redirect and token exchange. Null omits the audience parameter.
client_id:
type: string
description: The client_id used to identify this client at the issuer's token and authorization endpoints.
Expand All @@ -33789,6 +33816,11 @@ components:
type: string
description: The owning remote_session_issuer id.
format: uuid
scope:
type: array
items:
type: string
description: Explicit upstream OAuth scopes the dance requests for this client. Null falls back to the issuer's scopes_supported.
token_endpoint_auth_method:
type: string
description: How the client authenticates at the issuer's token endpoint. Null resolves to client_secret_basic at runtime.
Expand Down Expand Up @@ -36591,13 +36623,25 @@ components:
UpdateRemoteSessionClientForm:
type: object
properties:
audience:
type: string
description: Replace the upstream OAuth audience sent for this client. Omit to leave unchanged.
pattern: ^[!-~]+$
maxLength: 512
client_secret:
type: string
description: Rotate the client secret. Gram re-encrypts before persisting.
id:
type: string
description: The remote_session_client id.
format: uuid
scope:
type: array
items:
type: string
pattern: ^[!#-[\]-~]+$
maxLength: 128
description: Replace the explicit upstream OAuth scopes for this client. Omit to leave unchanged.
token_endpoint_auth_method:
type: string
description: Change how the client authenticates at the issuer's token endpoint.
Expand Down
10 changes: 5 additions & 5 deletions .speakeasy/workflow.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions client/sdk/.speakeasy/gen.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export type TokenEndpointAuthMethod = ClosedEnum<
* Form for cloning an oauth_proxy_provider's client credentials into a new remote_session_client. The caller supplies the existing oauth_proxy_provider, plus the remote_session_issuer and user_session_issuer to pair the new client with.
*/
export type CloneClientFromOAuthProxyProviderForm = {
/**
* Optional upstream OAuth audience to send on the authorize redirect and token exchange for the cloned client.
*/
audience?: string | undefined;
/**
* The oauth_proxy_provider to read client_id / client_secret from. Must live in the caller's project.
*/
Expand All @@ -32,6 +36,10 @@ export type CloneClientFromOAuthProxyProviderForm = {
* The remote_session_issuer the new client is registered with.
*/
remoteSessionIssuerId: string;
/**
* Explicit upstream OAuth scopes the dance should request for the cloned client. Omit to fall back to the issuer's scopes_supported.
*/
scope?: Array<string> | undefined;
/**
* How the cloned client authenticates at the issuer's token endpoint. Omit to default to client_secret_basic.
*/
Expand All @@ -49,8 +57,10 @@ export const TokenEndpointAuthMethod$outboundSchema: z.ZodMiniEnum<

/** @internal */
export type CloneClientFromOAuthProxyProviderForm$Outbound = {
audience?: string | undefined;
oauth_proxy_provider_id: string;
remote_session_issuer_id: string;
scope?: Array<string> | undefined;
token_endpoint_auth_method?: string | undefined;
user_session_issuer_id: string;
};
Expand All @@ -62,8 +72,10 @@ export const CloneClientFromOAuthProxyProviderForm$outboundSchema:
CloneClientFromOAuthProxyProviderForm
> = z.pipe(
z.object({
audience: z.optional(z.string()),
oauthProxyProviderId: z.string(),
remoteSessionIssuerId: z.string(),
scope: z.optional(z.array(z.string())),
tokenEndpointAuthMethod: z.optional(
TokenEndpointAuthMethod$outboundSchema,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export type CreateRemoteSessionClientFormTokenEndpointAuthMethod = ClosedEnum<
* Form for creating a remote_session_client. Caller supplies client_id (and optional client_secret) obtained out-of-band from the upstream issuer.
*/
export type CreateRemoteSessionClientForm = {
/**
* Optional upstream OAuth audience to send on the authorize redirect and token exchange.
*/
audience?: string | undefined;
/**
* client_id supplied by the caller.
*/
Expand All @@ -36,6 +40,10 @@ export type CreateRemoteSessionClientForm = {
* The owning remote_session_issuer id.
*/
remoteSessionIssuerId: string;
/**
* Explicit upstream OAuth scopes the dance should request for this client. Omit to fall back to the issuer's scopes_supported.
*/
scope?: Array<string> | undefined;
/**
* How the client authenticates at the issuer's token endpoint. Omit to default to client_secret_basic.
*/
Expand All @@ -55,9 +63,11 @@ export const CreateRemoteSessionClientFormTokenEndpointAuthMethod$outboundSchema

/** @internal */
export type CreateRemoteSessionClientForm$Outbound = {
audience?: string | undefined;
client_id: string;
client_secret?: string | undefined;
remote_session_issuer_id: string;
scope?: Array<string> | undefined;
token_endpoint_auth_method?: string | undefined;
user_session_issuer_id: string;
};
Expand All @@ -68,9 +78,11 @@ export const CreateRemoteSessionClientForm$outboundSchema: z.ZodMiniType<
CreateRemoteSessionClientForm
> = z.pipe(
z.object({
audience: z.optional(z.string()),
clientId: z.string(),
clientSecret: z.optional(z.string()),
remoteSessionIssuerId: z.string(),
scope: z.optional(z.array(z.string())),
tokenEndpointAuthMethod: z.optional(
CreateRemoteSessionClientFormTokenEndpointAuthMethod$outboundSchema,
),
Expand Down
10 changes: 10 additions & 0 deletions client/sdk/src/models/components/remotesessionclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export type RemoteSessionClientTokenEndpointAuthMethod = ClosedEnum<
* A remote_session_client record. client_secret_encrypted is never returned.
*/
export type RemoteSessionClient = {
/**
* Upstream OAuth audience sent on the authorize redirect and token exchange. Null omits the audience parameter.
*/
audience?: string | undefined;
/**
* The client_id used to identify this client at the issuer's token and authorization endpoints.
*/
Expand All @@ -49,6 +53,10 @@ export type RemoteSessionClient = {
* The owning remote_session_issuer id.
*/
remoteSessionIssuerId: string;
/**
* Explicit upstream OAuth scopes the dance requests for this client. Null falls back to the issuer's scopes_supported.
*/
scope?: Array<string> | undefined;
/**
* How the client authenticates at the issuer's token endpoint. Null resolves to client_secret_basic at runtime.
*/
Expand All @@ -74,6 +82,7 @@ export const RemoteSessionClient$inboundSchema: z.ZodMiniType<
unknown
> = z.pipe(
z.object({
audience: z.optional(z.string()),
client_id: z.string(),
client_id_issued_at: z.pipe(
z.iso.datetime({ offset: true }),
Expand All @@ -89,6 +98,7 @@ export const RemoteSessionClient$inboundSchema: z.ZodMiniType<
id: z.string(),
project_id: z.string(),
remote_session_issuer_id: z.string(),
scope: z.optional(z.array(z.string())),
token_endpoint_auth_method: z.optional(
RemoteSessionClientTokenEndpointAuthMethod$inboundSchema,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export type UpdateRemoteSessionClientFormTokenEndpointAuthMethod = ClosedEnum<
* Form for updating a remote_session_client. All non-id fields are optional patches.
*/
export type UpdateRemoteSessionClientForm = {
/**
* Replace the upstream OAuth audience sent for this client. Omit to leave unchanged.
*/
audience?: string | undefined;
/**
* Rotate the client secret. Gram re-encrypts before persisting.
*/
Expand All @@ -32,6 +36,10 @@ export type UpdateRemoteSessionClientForm = {
* The remote_session_client id.
*/
id: string;
/**
* Replace the explicit upstream OAuth scopes for this client. Omit to leave unchanged.
*/
scope?: Array<string> | undefined;
/**
* Change how the client authenticates at the issuer's token endpoint.
*/
Expand All @@ -51,8 +59,10 @@ export const UpdateRemoteSessionClientFormTokenEndpointAuthMethod$outboundSchema

/** @internal */
export type UpdateRemoteSessionClientForm$Outbound = {
audience?: string | undefined;
client_secret?: string | undefined;
id: string;
scope?: Array<string> | undefined;
token_endpoint_auth_method?: string | undefined;
user_session_issuer_id?: string | undefined;
};
Expand All @@ -63,8 +73,10 @@ export const UpdateRemoteSessionClientForm$outboundSchema: z.ZodMiniType<
UpdateRemoteSessionClientForm
> = z.pipe(
z.object({
audience: z.optional(z.string()),
clientSecret: z.optional(z.string()),
id: z.string(),
scope: z.optional(z.array(z.string())),
tokenEndpointAuthMethod: z.optional(
UpdateRemoteSessionClientFormTokenEndpointAuthMethod$outboundSchema,
),
Expand Down
Loading
Loading