简体   繁体   中英

How to consume Next.JS Rest Endpoints secured with Amplify from a React Native app

Background:

My current stack is a Next server to use as an admin portal and REST API for a Mobile App running with Expo - React Native. The Next Server is currently hosted as a Lambda@Edge.

I have secured both the Next server and the React Native app with AWS Amplify's withAuthenticator wrapper. (I also tried specific auth packages like Next-Auth and Expo's auth package)

Problem:

However, I can't figure out how to add the Auth info (Access_token) to my REST Requests from Mobile app -> Next Server

I tried adding the tokens as bearer headers to the API without luck after that I was fairly sure it all has to be set up and sent via cookies. BUT I am stuck on how to actually implement these cookies properly. I was hoping the endpoints:[] config could be used to set up my own domain to post to and handle the cookies. Reading the request on the server showed that it contained no Auth info when posted with this method.

Likewise using RTK Query (Preferably I add all the Auth to this instead of Amplify's API setup) I don't have the correct info to make an Authorized api request

Here are some snippets of the working page Authentication for both apps

API Endpoint /api/version:

import type { NextApiRequest, NextApiResponse } from 'next'
import { withSSRContext } from 'aws-amplify'

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse<Data | Error>,
) {
    const { Auth } = withSSRContext({req})

    try {
        const user = await Auth.currentAuthenticatedUser()
        return res.status(200).json({
            version: '1.0.0',
            user: user.username,
        })
    } catch (err) {
        console.log(err)
        return res.status(200).json({
            message: 'Unauthenticated',
        })
    }
}

Mobile App Config:

import {
    useAuthenticator,
    withAuthenticator,
} from '@aws-amplify/ui-react-native'
import { Amplify, Auth } from 'aws-amplify'
import awsconfig from './aws-exports'

Amplify.configure({
    ...awsconfig,
    API: {
        endpoints: [
            {
                name: 'MyApi',
                endpoint: 'http://NextIP:NextPort/api/',
            },
        ],
    },
})

Auth.configure(awsconfig)

export default withAuthenticator(App)

Mobile Screen:

import { API } from 'aws-amplify'

function getData() {
    const apiName = 'MyApi'
    const path = '/version'
    const myInit = {
        headers: {}, // OPTIONAL
    }

    return API.get(apiName, path, myInit)
}

export default function ModalScreen() {
    // Get token / Cookie for auth

    // const { data, isLoading, error } = useGetApiVersionQuery(null) // RTK Query
    getData() // Amplify
        .then(response => {
            console.log(response)
        })
        .catch(error => {
            console.log(error.response)
        })
    return ( <></>)}

I found a solution, however, could not get the Next-Auth middleware to fire when the token was sent using the Bearer token in headers. Which is my ideal way of handling the routes.

I wrapped the getToken({req}) call so that if there is no JWT Web token it would try encode the token separately Lastly ChatGpt somehow got me onto the package aws-jwt-verify which has everything you need to verify a token generated by aws-amplify/auth, in my case from react-native.

components/utils/auth.utils.ts

import { NextApiRequest } from 'next'
import { CognitoJwtVerifier } from 'aws-jwt-verify'
import { getToken } from 'next-auth/jwt'

// Verifier that expects valid token:
const verifier = CognitoJwtVerifier.create({
    userPoolId: process.env.COGNITO_USERPOOL_ID ?? '',
    tokenUse: 'id',
    clientId: process.env.COGNITO_CLIENT_ID ?? '',
    issuer: process.env.COGNITO_ISSUER ?? '',
})

export async function getMobileToken(req: NextApiRequest) {
    let token = null
    try {
        token = await getToken({ req })
    } catch (error) {
        console.log('Could not get JWT Web Token')
    }
    try {
        if (!token)
            token = await getToken({
                req,
                async decode({ token }) {
                    if (!token) return null
                    const decoded = await verifier.verify(token)
                    return decoded
                },
            })
    } catch (error) {
        return null
    }
    console.log('Mobile Token:', token)
    return token
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM