1<?php 2 3namespace OAuth\OAuth2\Service; 4 5use OAuth\Common\Consumer\CredentialsInterface; 6use OAuth\Common\Http\Client\ClientInterface; 7use OAuth\Common\Http\Exception\TokenResponseException; 8use OAuth\Common\Http\Uri\Uri; 9use OAuth\Common\Http\Uri\UriInterface; 10use OAuth\Common\Storage\TokenStorageInterface; 11use OAuth\OAuth2\Token\StdOAuth2Token; 12 13class GitHub extends AbstractService 14{ 15 /** 16 * Defined scopes, see http://developer.github.com/v3/oauth/ for definitions. 17 */ 18 19 /** 20 * Public read-only access (includes public user profile info, public repo info, and gists). 21 */ 22 const SCOPE_READONLY = ''; 23 24 /** 25 * Read/write access to profile info only. 26 * 27 * Includes SCOPE_USER_EMAIL and SCOPE_USER_FOLLOW. 28 */ 29 const SCOPE_USER = 'user'; 30 31 /** 32 * Read access to a user’s email addresses. 33 */ 34 const SCOPE_USER_EMAIL = 'user:email'; 35 36 /** 37 * Access to follow or unfollow other users. 38 */ 39 const SCOPE_USER_FOLLOW = 'user:follow'; 40 41 /** 42 * Read/write access to public repos and organizations. 43 */ 44 const SCOPE_PUBLIC_REPO = 'public_repo'; 45 46 /** 47 * Read/write access to public and private repos and organizations. 48 * 49 * Includes SCOPE_REPO_STATUS. 50 */ 51 const SCOPE_REPO = 'repo'; 52 53 /** 54 * Grants access to deployment statuses for public and private repositories. 55 * This scope is only necessary to grant other users or services access to deployment statuses, 56 * without granting access to the code. 57 */ 58 const SCOPE_REPO_DEPLOYMENT = 'repo_deployment'; 59 60 /** 61 * Read/write access to public and private repository commit statuses. This scope is only necessary to grant other 62 * users or services access to private repository commit statuses without granting access to the code. The repo and 63 * public_repo scopes already include access to commit status for private and public repositories, respectively. 64 */ 65 const SCOPE_REPO_STATUS = 'repo:status'; 66 67 /** 68 * Delete access to adminable repositories. 69 */ 70 const SCOPE_DELETE_REPO = 'delete_repo'; 71 72 /** 73 * Read access to a user’s notifications. repo is accepted too. 74 */ 75 const SCOPE_NOTIFICATIONS = 'notifications'; 76 77 /** 78 * Write access to gists. 79 */ 80 const SCOPE_GIST = 'gist'; 81 82 /** 83 * Grants read and ping access to hooks in public or private repositories. 84 */ 85 const SCOPE_HOOKS_READ = 'read:repo_hook'; 86 87 /** 88 * Grants read, write, and ping access to hooks in public or private repositories. 89 */ 90 const SCOPE_HOOKS_WRITE = 'write:repo_hook'; 91 92 /** 93 * Grants read, write, ping, and delete access to hooks in public or private repositories. 94 */ 95 const SCOPE_HOOKS_ADMIN = 'admin:repo_hook'; 96 97 /** 98 * Read-only access to organization, teams, and membership. 99 */ 100 const SCOPE_ORG_READ = 'read:org'; 101 102 /** 103 * Publicize and unpublicize organization membership. 104 */ 105 const SCOPE_ORG_WRITE = 'write:org'; 106 107 /** 108 * Fully manage organization, teams, and memberships. 109 */ 110 const SCOPE_ORG_ADMIN = 'admin:org'; 111 112 /** 113 * List and view details for public keys. 114 */ 115 const SCOPE_PUBLIC_KEY_READ = 'read:public_key'; 116 117 /** 118 * Create, list, and view details for public keys. 119 */ 120 const SCOPE_PUBLIC_KEY_WRITE = 'write:public_key'; 121 122 /** 123 * Fully manage public keys. 124 */ 125 const SCOPE_PUBLIC_KEY_ADMIN = 'admin:public_key'; 126 127 public function __construct( 128 CredentialsInterface $credentials, 129 ClientInterface $httpClient, 130 TokenStorageInterface $storage, 131 $scopes = [], 132 ?UriInterface $baseApiUri = null 133 ) { 134 parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri); 135 136 if (null === $baseApiUri) { 137 $this->baseApiUri = new Uri('https://api.github.com/'); 138 } 139 } 140 141 /** 142 * {@inheritdoc} 143 */ 144 public function getAuthorizationEndpoint() 145 { 146 return new Uri('https://github.com/login/oauth/authorize'); 147 } 148 149 /** 150 * {@inheritdoc} 151 */ 152 public function getAccessTokenEndpoint() 153 { 154 return new Uri('https://github.com/login/oauth/access_token'); 155 } 156 157 /** 158 * {@inheritdoc} 159 */ 160 protected function getAuthorizationMethod() 161 { 162 return static::AUTHORIZATION_METHOD_HEADER_TOKEN; 163 } 164 165 /** 166 * {@inheritdoc} 167 */ 168 protected function parseAccessTokenResponse($responseBody) 169 { 170 $data = json_decode($responseBody, true); 171 172 if (null === $data || !is_array($data)) { 173 throw new TokenResponseException('Unable to parse response.'); 174 } elseif (isset($data['error'])) { 175 throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '"'); 176 } 177 178 $token = new StdOAuth2Token(); 179 $token->setAccessToken($data['access_token']); 180 // Github tokens evidently never expire... 181 $token->setEndOfLife(StdOAuth2Token::EOL_NEVER_EXPIRES); 182 unset($data['access_token']); 183 184 $token->setExtraParams($data); 185 186 return $token; 187 } 188 189 /** 190 * Used to configure response type -- we want JSON from github, default is query string format. 191 * 192 * @return array 193 */ 194 protected function getExtraOAuthHeaders() 195 { 196 return ['Accept' => 'application/json']; 197 } 198 199 /** 200 * Required for GitHub API calls. 201 * 202 * @return array 203 */ 204 protected function getExtraApiHeaders() 205 { 206 return ['Accept' => 'application/vnd.github.v3+json']; 207 } 208 209 /** 210 * {@inheritdoc} 211 */ 212 protected function getScopesDelimiter() 213 { 214 return ','; 215 } 216} 217