Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add pkce-flow to battledotnet oauth #19

Closed
wants to merge 3 commits into from
Closed
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
44 changes: 39 additions & 5 deletions src/runtime/server/lib/oauth/battledotnet.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { H3Event, H3Error } from 'h3'
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from 'h3'
import { ofetch } from 'ofetch'
import { withQuery, parsePath } from 'ufo'
import { withQuery, parsePath, getQuery as ufoGetQuery } from 'ufo'
import { defu } from 'defu'
import { useRuntimeConfig } from '#imports'
import { randomUUID } from 'crypto'
import { randomUUID, createHash } from 'crypto'

export interface OAuthBattledotnetConfig {
/**
Expand Down Expand Up @@ -79,7 +79,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }: OAuthCo
if (!onError) throw error
return onError(event, error)
}

if (!code) {
config.scope = config.scope || ['openid']
config.region = config.region || 'EU'
Expand All @@ -89,6 +89,13 @@ export function battledotnetEventHandler({ config, onSuccess, onError }: OAuthCo
config.tokenURL = 'https://oauth.battlenet.com.cn/token'
}

// PKCE flow
const codeVerifier = randomUUID()
const hash = createHash('sha3-256')
const codeChallenge = hash.update(codeVerifier).digest('base64')

await useStorage().setItem('codeVerifierBattledotnet', codeVerifier)

// Redirect to Battle.net Oauth page
const redirectUrl = getRequestURL(event).href
return sendRedirect(
Expand All @@ -97,7 +104,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }: OAuthCo
client_id: config.clientId,
redirect_uri: redirectUrl,
scope: config.scope.join(' '),
state: randomUUID(), // Todo: handle PKCE flow
state: codeChallenge, // Todo: handle PKCE flow
response_type: 'code',
})
)
Expand All @@ -109,6 +116,33 @@ export function battledotnetEventHandler({ config, onSuccess, onError }: OAuthCo
config.scope.push('openid')
}

const { state } = ufoGetQuery(redirectUrl)

if (!state) {
const error = createError({
statusCode: 401,
message: 'State is required',
data: query
})
if (!onError) throw error
return onError(event, error)
}

const codeVerifier = await useStorage().getItem('codeVerifierBattledotnet')

const hash = createHash('sha3-256')
const codeChallenge = hash.update(codeVerifier as string).digest('base64')

if (codeChallenge !== (state as string).replace(/\s/g, '+')) {
const error = createError({
statusCode: 401,
message: 'State is not valid',
data: query
})
if (!onError) throw error
return onError(event, error)
}

const authCode = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString('base64')

const tokens: any = await $fetch(
Expand All @@ -128,7 +162,7 @@ export function battledotnetEventHandler({ config, onSuccess, onError }: OAuthCo
}
).catch((error) => {
return { error }
})
})

if (tokens.error) {
const error = createError({
Expand Down