Skip to content

Commit

Permalink
Fix; Dig deeper for email addresses on Github
Browse files Browse the repository at this point in the history
The /user API returns only the data the user explicitly set as public and null for those he didn't.

If the user did not set the email address public (me for example), the email address being mandatory for the process, the authentication fails returning an error message.

By using the /user/emails API in conjunction, it is possible to retrieve all the email addresses the user set in Github, regardless if they are set public or not, and select one of those (ex: primary one), for the login process to complete successfully.
  • Loading branch information
Zoly authored Jul 22, 2024
1 parent 6d42536 commit 37fc818
Showing 1 changed file with 49 additions and 5 deletions.
54 changes: 49 additions & 5 deletions src/Libraries/GithubOAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

class GithubOAuth extends AbstractOAuth
{
public static string $API_CODE_URL = 'https://github.com/login/oauth/authorize';
public static string $API_TOKEN_URL = 'https://github.com/login/oauth/access_token';
public static string $API_USER_INFO_URL = 'https://api.github.com/user';
private static string $APPLICATION_NAME = 'ShieldOAuth';
public static string $API_CODE_URL = 'https://github.com/login/oauth/authorize';
public static string $API_TOKEN_URL = 'https://github.com/login/oauth/access_token';
public static string $API_USER_INFO_URL = 'https://api.github.com/user'; // The /user API returns the user's publicly visible data or null for those that are not set
public static string $API_USER_EMAILS_URL = 'https://api.github.com/user/emails'; // The /user/emails API returns all email addresses for the user, including those that are not set public
private static string $APPLICATION_NAME = 'ShieldOAuth';
protected string $token;
protected CURLRequest $client;
protected ShieldOAuthConfig $config;
Expand Down Expand Up @@ -89,7 +90,50 @@ protected function fetchUserInfoWithToken(): object
exit($e->getMessage());
}

return json_decode($response->getBody());
$userInfo = json_decode($response->getBody(), false);

// the email address is mandatory
if (empty($userInfo->email)) {
$userInfo->email = $this->getUserPrimaryEmail($this->fetchUserEmailsWithToken());
}

return $userInfo;
}

/**
* @return list<object>
*/
protected function fetchUserEmailsWithToken(): array
{
// send request to API URL
try {
$response = $this->client->request('GET', self::$API_USER_EMAILS_URL, [
'headers' => [
'User-Agent' => self::$APPLICATION_NAME . '/1.0',
'Accept' => 'application/vnd.github+json',
'Authorization' => 'Bearer ' . $this->getToken(),
],
'http_errors' => false,
]);
} catch (Exception $e) {
exit($e->getMessage());
}

return json_decode($response->getBody(), false);
}

/**
* @param list<object> $emailAddresses
*/
protected function getUserPrimaryEmail(array $emailAddresses): string
{
// try to get the one marked as primary, otherwise grab the first one
if (! empty($emailAddresses)) {
$primaryEmail = array_filter($emailAddresses, static fn ($eMail) => $eMail->primary);
$userEmail = ! empty($primaryEmail) ? array_shift($primaryEmail)->email : array_shift($emailAddresses)->email;
}

return $userEmail ?? '';
}

protected function setColumnsName(string $nameOfProcess, $userInfo): array
Expand Down

0 comments on commit 37fc818

Please sign in to comment.