Closed JoaquimLey closed 3 years ago
OK, after digging quite a bit I've noticed what I think might be the culprit, using Apple's JS button the response_type
query param has both the code
and id_token
(Apple's JS does not call the GET
request to my own API to fetch the URL for apple).
response_type=code%20id_token
While using this passport library (with the above implementation):
response_type=code
@ananay any pointers?
I could not solve this issue with the library instead I directly called apple from the frontend, on my strategy I changed it to:
(jwt is an abstraction to '@nestjs/jwt'
/jsonwebtoken
library)
async validate(
req: any,
accessToken: string,
refreshToken: string,
idToken: any,
profile: any,
verified: any
): Promise<UserFromAuthProvider> {
const body = req.body
const userProfile = body.user ? JSON.parse(body.user) : null
const decodedToken = this.jwt.decode(body.id_token)
// The UserFromAuthProvider is an abstraction/model specific to my project, you can return wtv you want.
const user = <UserFromAuthProvider>{
id: accessToken,
email: decodedToken.email,
first_name: userProfile ? userProfile.name.firstName : null,
last_name: userProfile ? userProfile.name.lastName : null,
picture_url: null,
provider_token: decodedToken.sub
}
return user
}
Hope this helps someone that has the same or similar issue.
@JoaquimLey response_type
is is written code
by jaredhanson/passport-oauth2
at https://github.com/jaredhanson/passport-oauth2/blob/ee3fe9f17c0f3a90f2d9d938f267e9942b9fba49/lib/strategy.js#L229
@zfanta I didn't really get what you meant?
@zfanta @JoaquimLey any other solution for the above instead of doing it from frontend? I have faced the same issue in which i am not getting the user data in return.
@JoaquimLey @AkshayCloudAnalogy to prevent https://github.com/jaredhanson/passport-oauth2/blob/ee3fe9f17c0f3a90f2d9d938f267e9942b9fba49/lib/strategy.js#L229 from overwriting response_type
to code
, I manually wrote authenticate
method.
@Injectable()
export class AppleStrategy extends PassportStrategy(Strategy, 'apple') {
constructor (...) {
super({...})
}
async validate (@Req() req: Request): Promise<any> {
...
}
authenticate (req: Request, options?: object): void {
if (req.body.code !== undefined) {
super.authenticate(req, {
...options
})
} else {
const params = new URLSearchParams()
params.append('property', 'user')
params.append('response_type', 'code id_token')
params.append('scope', 'email')
params.append('response_mode', 'form_post')
params.append('redirect_uri', <return url>)
params.append('client_id', <client_id>)
req.res?.writeHead(302, {
Location: `https://appleid.apple.com/auth/authorize?${params.toString()}`
})
req.res?.end()
}
}
}
Thanks. This solved the issue.
@zfanta I did not test this as for my use case I'm fine calling apple from the frontend but I'll definitely keep this in mind if in the future I need to redirect the call through a safe environment.
Here's my strategy (https://docs.nestjs.com/security/authentication):
And this is my server output
So there's definitely something wrong with the validate/callback function at least on NestJS's passport wrapper implementation, I've spent a lot of time trying to figure the way but for some reason, I can't.
I know the user is not returned all the time, but even at the first sign in the "profile" which is clearly mapped to the callback function has nothing there, only the body has a
user {}
object. My issue here I can't wrap my head around how I should decode the AT or RT to get anything that maps to the user (these change on each request).Also, the req is always passed to the callback regardless of the option set above.
For some context, here's my (working) google counterpart using passport-google-oauth20
thank you in advance for your time