DEV Community

koreanDev
koreanDev

Posted on

Debugging a 400 Error in Sign in with Apple Token Exchange

Today, I submitted a new build of my app to App Store Connect.

While working on the Apple Sign In integration for this Flutter project, I ran into a server-side issue during the token exchange process.

For a long time, I left this project without a login feature.

In the beginning of this project, I wanted users to access all features freely, without strict authentication.

In the Google Play version, users can sign in with Google,

but the App Store version didn’t support Apple Sign In.

At the time, this was just a personal project.

After releasing it, I got a job and didn’t really have time to maintain or improve it.

So the Google login stayed,

but Apple Sign In was never properly implemented.

Anyway, I finally decided to implement it.

I went back to my legacy code.

Some parts were already there, except the withdrawal flow.

I implemented Apple Sign In, logout, and auto-login.

(If Apple Sign In is successful, my Node.js server responds with an access token to the Flutter front end, and the user can access the main page until the token expires without logging in every time.)

But the withdrawal feature still didn’t work properly.

According to App Store guidelines,

developers must provide a proper way for users to delete their accounts.

If you add Apple Sign In, you must also provide an account deletion option.

Anyway, getting back to my project.

The /apple/login API on my Node.js server was responding with a null value,

which should have been the refresh token.

I needed to investigate which part was causing this problem.

Blow is part of my function

// /apple/login api

if (authorizationCode) {

const clientSecret = generateAppleClientSecret();

const params = qs.stringify({

client_id: process.env.APPLE_CLIENT_ID!,

client_secret: clientSecret,

code: authorizationCode,

grant_type: ‘authorization_code’,

Become a Medium member
redirect_uri: ‘https://MyfireBaseProject/apple_redirect',

});

const tokenResponse = await axios.post(

https://appleid.apple.com/auth/oauth2/v2/token',

params,

{ headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ } }

);

const refreshToken = tokenResponse.data.refresh_token;

}

The code was showing this error:

Apple authorizationCode token request failed:

AxiosError: Request failed with status code 400.

The tokenResponse.data.refresh_token value was null.

First of all, when a user logs in successfully on Flutter using the Sign in with Apple package,

it returns an authorizationCode.

With this authorizationCode, the server exchanges it with Apple

to obtain an access token and a refresh token.

(and other parameters required for the revoke API).

For revocation, Apple identifies the user through the refresh token and other parameters.

At the beginning,

I investigated whether those parameters were valid,

such as the clientSecret and Apple client Id.

But they were all valid.

I actually spent quite a lot of time on this, stupidly haha.

Finally, I found out what the problem was.

It was just a human error.

I had simply used the wrong endpoint in the axios.post request.

Like this:

const tokenResponse = await axios.post(

https://appleid.apple.com/auth/oauth2/v2/token', // wrong

// ‘https://appleid.apple.com/auth/token', // correct

I just asked ChatGPT about that specific API endpoint instead of double-checking Apple’s official documentation myself.

It suggested an endpoint that actually belongs to the Roster API, which is meant for a different purpose.

After switching to the correct Sign in with Apple token endpoint, everything worked fine.

Top comments (0)