To zoom out a little - for all services that we allow you to log in with, they use the industry standard OAuth2 protocol with a scope parameter that lets us (Kagi) tell the service what datapoints we want. For example, for both Google and Apple, we only request the email
scope:
"https://accounts.google.com/o/oauth2/v2/auth?client_id=#{google_client_id}&redirect_uri=#{url}&response_type=code&scope=email&access_type=online&state=#{state}"
"https://appleid.apple.com/auth/authorize?response_type=code%20id_token&client_id=#{apple_client_id}&redirect_uri=#{url}&response_mode=form_post&scope=email&state=#{state}"
This is the URL you get sent to when you click the icons on our login form, which then takes you to the third party for you to review and either grant or deny whatever we are asking for.
In return, the provider gives us back an "API key" where we can access only the information that we requested & you consented to.
On Apple specifically:
We don't use the cited Apple JS. We implement the OAuth2 flow ourselves on our backend. We get the response as detailed in the list here: https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773
The JWT itself contains an email
field once decoded, and that is what we use.
On google specifically: Same deal. We implement the same standard OAuth2 flow, only requesting the email
scope.
We then issue a request using the token to /userinfo/v2/me
. At the moment I cannot for the life of me find the docs explaining what is/isn't returned for this scope at the moment, but I just dumped the response that I got from their API locally:
{
"id": "113748462741969226722",
"email": "z...@gmail.com",
"verified_email": true,
"picture": "https://lh3.googleusercontent.com/..."
}
and similarly we only pluck the email from this response.