Skip to content

Commit

Permalink
refactor: use a common secret checker
Browse files Browse the repository at this point in the history
  • Loading branch information
vnugent committed Nov 11, 2024
1 parent 5bf7103 commit 993bd1b
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 29 deletions.
35 changes: 6 additions & 29 deletions src/app/api/mobile/login/route.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
import { NextRequest, NextResponse } from 'next/server'
import * as Auth0 from 'auth0'

import { AUTH_CONFIG_SERVER } from '../../../../Config'

if (AUTH_CONFIG_SERVER == null) throw new Error('AUTH_CONFIG_SERVER not defined')

const mobileAuthSecret = process.env.MOBILE_AUTH_SECRET
if (mobileAuthSecret == null) {
console.warn('Mobile auth secret not found')
}

const { clientSecret, clientId, issuer } = AUTH_CONFIG_SERVER

// Set up Auth0 client
const auth = new Auth0.AuthenticationClient({
domain: issuer.replace('https://', ''),
clientId,
clientSecret
})
import { auth0Client, isNullOrEmpty } from '@/js/auth/mobile'
import { withMobileAuth } from '@/js/auth/withMobileAuth'

/**
* Mobile login handler
*/
export async function POST (request: NextRequest): Promise<any> {
const authHeader = request.headers.get('User-Agent')
if (mobileAuthSecret != null && authHeader !== mobileAuthSecret) {
return NextResponse.json({ message: 'Unauthorized', status: 401 })
}

let username, password: string
async function postHandler (request: NextRequest): Promise<NextResponse> {
let username: string, password: string
try {
const data = await request.json()
username = data.username
Expand All @@ -44,7 +23,7 @@ export async function POST (request: NextRequest): Promise<any> {

let response: Auth0.JSONApiResponse<Auth0.TokenSet> | undefined
try {
response = await auth.oauth.passwordGrant({
response = await auth0Client.oauth.passwordGrant({
username,
password,
scope: 'openid profile email offline_access',
Expand All @@ -58,6 +37,4 @@ export async function POST (request: NextRequest): Promise<any> {
}
}

function isNullOrEmpty (str: string | null | undefined): boolean {
return str == null || str?.trim() === ''
}
export const POST = withMobileAuth(postHandler)
37 changes: 37 additions & 0 deletions src/app/api/mobile/refreshToken/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from 'next/server'
import * as Auth0 from 'auth0'
import { auth0Client, isNullOrEmpty } from '@/js/auth/mobile'
import { withMobileAuth } from '@/js/auth/withMobileAuth'

/**
* Mobile refresh token handler
*/
async function postHandler (request: NextRequest): Promise<any> {
let refreshToken: string
try {
const data = await request.json()
refreshToken = data.refreshToken

if (isNullOrEmpty(refreshToken)) {
console.error('Empty refreshToken!')
throw new Error('Invalid payload')
}
} catch (error) {
return NextResponse.json({ error: 'Unexpected error', status: 400 })
}

let response: Auth0.JSONApiResponse<Auth0.TokenSet> | undefined
try {
response = await auth0Client.oauth.refreshTokenGrant({
refresh_token: refreshToken,
audience: 'https://api.openbeta.io'
})

return NextResponse.json({ data: response.data })
} catch (error) {
console.error('#### Auth0 error ####', error)
return NextResponse.json({ error: 'Unexpected auth error', status: 403 })
}
}

export const POST = withMobileAuth(postHandler)
22 changes: 22 additions & 0 deletions src/js/auth/mobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as Auth0 from 'auth0'

import { AUTH_CONFIG_SERVER } from '../../Config'

if (AUTH_CONFIG_SERVER == null) throw new Error('AUTH_CONFIG_SERVER not defined')

if (process.env.MOBILE_AUTH_SECRET == null) {
console.warn('Mobile auth secret not found')
}

const { clientSecret, clientId, issuer } = AUTH_CONFIG_SERVER

// Set up Auth0 client
export const auth0Client = new Auth0.AuthenticationClient({
domain: issuer.replace('https://', ''),
clientId,
clientSecret
})

export const isNullOrEmpty = (str: string | null | undefined): boolean => {
return str == null || str?.trim() === ''
}
22 changes: 22 additions & 0 deletions src/js/auth/withMobileAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NextRequest, NextResponse } from 'next/server'

const mobileAuthSecret = process.env.MOBILE_AUTH_SECRET

type Next13ApiHandler = (req: NextRequest) => Promise<NextResponse>

/**
* A high-order function to protect mobile-related auth endpoints.
* Do not use elsewhere.
*/
export const withMobileAuth = (handler: Next13ApiHandler): Next13ApiHandler => {
return async function (request: NextRequest) {
if (request.method !== 'POST') {
return NextResponse.json({ message: 'Must send POST request', status: 405 })
}
const authHeader = request.headers.get('Secret')
if (mobileAuthSecret != null && authHeader === mobileAuthSecret) {
return await handler(request)
}
return NextResponse.json({ message: 'Unauthorized', status: 401 })
}
}

0 comments on commit 993bd1b

Please sign in to comment.