Skip to content

AWS AppSync GraphQL subscriptions with OIDC authMode - UnauthorizedException #14587

@arttu-pekkarinen

Description

@arttu-pekkarinen

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

GraphQL API

Amplify Version

v6

Amplify Categories

auth

Backend

CDK

Environment information

# Put output below this line


Describe the bug

I'm trying to use AWS AppSync GraphQL subscriptions with OIDC authentication mode using @aws-amplify/api [generateClient], but getting UnauthorizedException for WebSocket connections while HTTP queries/mutations work fine with the same OIDC token. For OIDC I have defined a custom token provider: MSAL

My workaround now is to create a "custom" websocket client for subscriptions. It works when following the documented protocol for providing headers for websocket client connection:

https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html

All this seems odd because subscriptions has worked when I've done this with cognito. So this makes me wonder whether this is supported at all, although it's not explicitly said not anywhere in the documentation, or am I doing this wrong?

Expected behavior

Appsync subscriptions with websocket should work when amplify authorization mode set to OIDC with custom token provider

Reproduction steps

  1. Configure amplify appsync to use oidc authorization mode
  2. Call for appsync subscription

Connection failed: UnauthorizedException

Code Snippet

config.ts

import { Amplify } from 'aws-amplify'
import { msalTokenProvider } from './providers/msalTokenProvider'

const awsconfig = {
  API: {
    GraphQL: {
      endpoint: 'MY_ENDPOINT '',
      region: 'eu-central-1',
      defaultAuthMode: 'oidc' as const,
    },
  },
}

Amplify.configure(awsconfig, {
  Auth: {
    tokenProvider: msalTokenProvider
  }
})

export default Amplify`

msalTokenProvider.ts

import type { AccountInfo, IPublicClientApplication } from '@azure/msal-browser'
import { TokenProvider, decodeJWT } from 'aws-amplify/auth'

let msalInstance: IPublicClientApplication | null = null
let currentAccount: AccountInfo | null = null

export const msalTokenProvider: TokenProvider = {
  async getTokens({ forceRefresh } = {}) {
    if (!msalInstance || !currentAccount) {
      throw new Error('MSAL not initialized. Call configureMsalTokenProvider first.')
    }

    const response = await msalInstance.acquireTokenSilent({
      scopes: ['openid', 'profile', 'email'],
      account: currentAccount,
      forceRefresh
    })

    return {
      accessToken: decodeJWT(response.accessToken!),
      idToken: decodeJWT(response.idToken!),
    }
  },
}

// Function to initialize the TokenProvider with MSAL instance and account
export function configureMsalTokenProvider(instance: IPublicClientApplication, account: AccountInfo) {
  msalInstance = instance
  currentAccount = account
}

export type { TokenProvider }

Log output

// Put your logs below this line


aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    AppSyncRelated to AppSync issuesbugSomething isn't workingpending-maintainer-responseIssue is pending a response from the Amplify team.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions