Skip to content

Conversation

@TylerLeonhardt
Copy link
Member

Fixes #260156

Here's what a WWW-Authenticate header looks like from the very real Azure situation:

WWW-Authenticate: Bearer
    realm="",
    authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize",
    error="insufficient_claims",
    claims="eyJhY2Nlc3NfdG9rZW4iOnsiYWNycyI6eyJlc3NlbnRpYWwiOnRydWUsInZhbHVlcyI6WyJwMSJdfX19"

Note: put on newlines for readibility

This generically looks like this:

WWW-Authenticate: <challenge>
// or
WWW-Authenticate: <challenge1>,..., <challengeN>

A challenge looks like this:

challenge = <auth-scheme> <auth-param>, …, <auth-paramN>

Where:

  • auth-scheme is a case-insensitive string
  • auth-param is key=value where key and value are strings (key is unique within the challenge)

which in TypeScript becomes:

interface AuthenticationChallenge {
  scheme: string
  params: Record<string, string>
}

Regardless of all of that, the API for "extension that wants auth" is as simple as possible... and all about simply passing the WWW-Authenticate value into the Auth API so the extension doesn't have to be in the business of parsing this complex string:

AuthenticationSessionRequest {
  scopes?: string[];
  challenge: string;
}

declare module 'vscode' {
  export namespace authentication {
    export function getSession(providerId: string, scopeListOrRequest: ReadonlyArray<string> | AuthenticationSessionRequest, options?: AuthenticationGetSessionOptions): Thenable<AuthenticationSession | undefined>;
  }
}

In other words:

vscode.authentication.getSession(
  'microsoft',
  {
    scopes: // can be supplied otherwise will be inferred from the challenge
    challenge: // literal string from WWW-Authenticate header in the request that failed
  }
  options); // same options

This shape let's us support:

  • An AuthenticationSession needs to be re-created (like if there are claims in the challenge (aka metadata inside of the token that arent scopes)
  • A completely new session needs to be created because more scopes are required. (ex: MCP will use this when you use a tool that needs more permissions than what the token can do

The challenge becomes the star, and since the auth extension is stateless, it will need to know the scopes so the option to provide them is there if needed.

extension supplying auth perspective

From the "extension supplying auth perspective" we are starting with this API but it doesn't need to be finalized at the same time as the above:

AuthProvider implements two new functions:

export interface AuthenticationProviderOptions {
  supportsChallenges?: boolean;
}

interface AuthenticationChallenge {
  scheme: string
  params: Record<string, string>
}
interface AuthenticationConstraint {
  readonly challenges: readonly AuthenticationChallenge[];
  readonly scopes?: readonly string[];
}

interface AuthenticationProvider {
  getSessionsFromChallenges?(constraint: AuthenticationConstraint, options: AuthenticationProviderSessionOptions): Thenable<AuthenticationSession[]>;
  createSessionFromChallenges?(constraint: AuthenticationConstraint, options: AuthenticationProviderSessionOptions): Thenable<AuthenticationSession>;
}

This lets the Auth Provider Opt-in to supporting challenges.

@akadevbarki76-collab
Copy link

im back glad be vscoder

@TylerLeonhardt TylerLeonhardt merged commit cf433b5 into main Aug 15, 2025
33 checks passed
@TylerLeonhardt TylerLeonhardt deleted the tyler/future-flyingfish branch August 15, 2025 01:10
@vs-code-engineering vs-code-engineering bot locked and limited conversation to collaborators Sep 29, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Need a way to support "challenges"

4 participants