Repository: PyGithub/PyGithub

Issue:
Bad Credentials, token works fine with `curl`
Hey folks, 

Used a personal access token as authentication, yet it does not work through python. No issues when using same token with curl, however here is the issue when using it in python.

```python
>>> gh = Github(token)
>>> gh.get_user("finnkauski")
GET https://api.github.com/users/finnkauski {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 401 {'date': 'Thu, 12 Nov 2020 12:49:57 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '80', 'server': 'GitHub.com', 'status': '401 Unauthorized', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '60', 'x-ratelimit-remaining': '45', 'x-ratelimit-reset': '1605187930', 'x-ratelimit-used': '15', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '1; mode=block', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': 'E402:5A85:2910526:2E6F2D3:5FAD2F75'} {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/MainClass.py", line 271, in get_user
    headers, data = self.__requester.requestJsonAndCheck(
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/Requester.py", line 317, in requestJsonAndCheck
    return self.__check(
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/Requester.py", line 342, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.BadCredentialsException: 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
>>> 

```

Meanwhile in curl:

```
>>> curl -H "Authorization: token <token>" https://api.github.com/users/finnkauski
{
  "login": "finnkauski",
  "id": 15175738,
  ... 
}
```

I honestly have no clue what's up. I went into the source and found the `Requester` code and it looks like it generates the header correctly. But for some reason it doesn't really fly. 

I am using version: `1.53` 


Discussion:
Reinstalled my virtual environment, reinstalled all the packages:
```python
>>> from github import Github
>>> token = "thisismytokenfromdevelopersettingsongithub"
>>> gh = Github(token)
>>> gh.get_user("finnkauski")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/MainClass.py", line 271, in get_user
    headers, data = self.__requester.requestJsonAndCheck(
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/Requester.py", line 317, in requestJsonAndCheck
    return self.__check(
  File "/Users/arturas/projects/gh_wrangler/.venv/lib/python3.8/site-packages/github/Requester.py", line 342, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.BadCredentialsException: 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
>>>
```

**Edit**
Also just restarted the machine, no luck (obviously last hope here).
Cloned my development repo on my personal machine running Ubuntu, no issues, works first try. 
```
>>> import github
>>> g = github.Github("mytoken")
>>> github.enable_console_debug_logging()
>>> g.get_repo("PyGithub/PyGithub")
GET https://api.github.com/repos/PyGithub/PyGithub {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 200 {'date': 'Fri, 13 Nov 2020 06:54:18 GMT' ...
```

What do you see being sent when you enable debug logging?
Also, take a look at the scope of the token. Make sure `read:user` is checked.
Got the same problem using an Access token with everything checked.
- OSX 10.14, 
- python3.9, (also tried 3.8 and 3.7 from brew)
- PyGithub 1.54.1 (Tried downgrading version until 1.43, but non worked. Also py2 did not work)

Also script and token works on a centos7 (py3.6) machine, so it seems to be an os related issue

```
$ python3 test.py
GET https://api.github.com/repos/PyGithub/PyGithub {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 401 {'server': 'GitHub.com', 'date': 'Wed, 14 Apr 2021 07:22:42 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '80', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '60', 'x-ratelimit-remaining': '44', 'x-ratelimit-reset': '1618387316', 'x-ratelimit-used': '16', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': 'DFDB:03C3:23A1503:246AC37:60769842'} {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
Traceback (most recent call last):
  File "/test.py", line 10, in <module>
    repo = g.get_repo("PyGithub/PyGithub")
  File "/usr/local/lib/python3.9/site-packages/github/MainClass.py", line 345, in get_repo
    headers, data = self.__requester.requestJsonAndCheck(
  File "/usr/local/lib/python3.9/site-packages/github/Requester.py", line 315, in requestJsonAndCheck
    return self.__check(
  File "/usr/local/lib/python3.9/site-packages/github/Requester.py", line 340, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.BadCredentialsException: 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
```

Curl works fine:
```
$ curl -s -H "Authorization: token ghp_*******" https://api.github.com/repos/PyGithub/PyGithub | jq .full_name
"PyGithub/PyGithub"
```

Pip output:
```
$ pip3 install --upgrade PyGithub
Requirement already satisfied: PyGithub in /usr/local/lib/python3.9/site-packages (1.54.1)
Requirement already satisfied: deprecated in /usr/local/lib/python3.9/site-packages (from PyGithub) (1.2.12)
Requirement already satisfied: requests>=2.14.0 in /usr/local/lib/python3.9/site-packages (from PyGithub) (2.24.0)
Requirement already satisfied: pyjwt<2.0 in /usr/local/lib/python3.9/site-packages (from PyGithub) (1.7.1)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.9/site-packages (from requests>=2.14.0->PyGithub) (1.25.11)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.9/site-packages (from requests>=2.14.0->PyGithub) (2.10)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.9/site-packages (from requests>=2.14.0->PyGithub) (2020.12.5)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.9/site-packages (from requests>=2.14.0->PyGithub) (3.0.4)
Requirement already satisfied: wrapt<2,>=1.10 in /usr/local/lib/python3.9/site-packages (from deprecated->PyGithub) (1.12.1)
```
@maikelpoot And how are you passing the token to the Github() object?
Tried both `g = github.Github(login_or_token="ghp_")` as `g = github.Github("ghp_")` but makes no difference.

Also took a dive in to `Requester.py` to add some debug prints, but the headers given to `cnx.request in __requestRaw` seem to be fine
Add `user_agent` to your constructor and see if changing that helps any? Although why that would, I have no idea ...
Had tried that before, makes no difference but the user-agent in the debug logging :-) 

```
GET https://api.github.com/repos/PyGithub/PyGithub {'Authorization': 'token (oauth token removed)', 'User-Agent': 'myscript'} None ==> 401 {'s
```
I'm also seeing what feels like bogus ``Bad Credentials`` errors, for example:
```
DEBUG:urllib3.connectionpool:https://api.github.com:443 "GET /repos/ansible-collections/community.general/issues/2489 HTTP/1.1" 401 80
2021-06-04 12:47:32,638 DEBUG 696800 connectionpool.py:_make_request:433 https://api.github.com:443 "GET /repos/ansible-collections/community.general/issues/2489 HTTP/1.1" 401 80
ERROR:root:401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
2021-06-04 12:47:32,639 ERROR 696800 github.py:inner:164 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
ERROR:root:Uncaught exception
Traceback (most recent call last):
  File "/home/dmsimard/dev/git/ansible-community/collection_bot/ansibullbot/decorators/github.py", line 134, in inner
    x = fn(*args, **kwargs)
  File "/home/dmsimard/dev/git/ansible-community/collection_bot/ansibullbot/wrappers/ghapiwrapper.py", line 201, in get_issue
    if issue.update():
  File "/home/dmsimard/dev/virtualenvs/ansibullbot/lib64/python3.9/site-packages/github/GithubObject.py", line 331, in update
    headers, data = self._requester._Requester__check(
  File "/home/dmsimard/dev/virtualenvs/ansibullbot/lib64/python3.9/site-packages/github/Requester.py", line 378, in __check
    raise self.__createException(status, responseHeaders, output)
github.GithubException.BadCredentialsException: 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}
```

Whereas the same query with the same token works with curl:
``curl -s -H "Authorization: token ghp_*****" https://api.github.com/repos/ansible-collections/community.general/issues/2489 | jq``

What's weird is that, to my knowledge, it used to work until I deleted the token and created a new one.

At first I thought it could be related to GitHub's [new token format](https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/) but when I tried another "old" token I still had, it also failed with Bad Credentials so I figure the issue might be elsewhere.
So I wonder if this is due to redirects -- could you try with https://github.com/PyGithub/PyGithub/commit/1265747e992ba7d34a469b6b2f527809f8bf7067 applied (you can ignore the test) and see if that helps any?
I came back to the project I was working on and it suddenly worked again without (to my knowledge) changing anything of significance -- I tried creating a new token and now I can reproduce the "Bad Credentials" issue again.

I deleted the previous token that worked but in hindsight I shouldn't have done that to do more troubleshooting. It's a token I had just created last week. Could there be some sort of propagation period or lag before the token starts working everywhere ?

I will investigate some more and find out if I can get to the bottom of the issue.

Edit: btw applying https://github.com/PyGithub/PyGithub/commit/1265747e992ba7d34a469b6b2f527809f8bf7067 doesn't resolve the issue
I spent some more time on this and it may turn out that the issue was caused by a caching behavior in the tool that I am running where it may cache prior tokens. I'll report back if I find out that the issue is related to pygithub but it doesn't look to be the case. Sorry for the noise !
Running into same issue here. Using PyGithub 1.55

The issue seems to be intermittent - so it would work one time, but won't work next run a few seconds later, then it won't work for X times, and then it might work eventually. Very unreliable = no use.
Something is not working well here. I've been trying to use PyGithub with a new token, it has every permission it needs, works properly for the same query in curl, yet it fails with PyGithub.

edit: After some debugging into the library, it seems that the `__authorizationHeader` originally set and the actually sent request's `Authorization` headers are different. The first uses a `token <token>` value with the token value I've supplied, but the second one uses a `Basic` auth with a value I haven't recognized after decoding. But after reading [this answer on SO](https://stackoverflow.com/a/60451840/238845), I realized that the same header dropping might be happening here, and indeed, the token is what I had specified in my `.netrc` file. Once I've updated that, things started to work.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I am having similar issues, thanks to the comment by @scorchio I was able to figure it out: My code was trying to fetch the branch `master`, which was redirected to `main`, but the second request resulted in a `Bad credentials`-error:

```
| GET https://api.github.com/repos/totycro/stacs/branches/master {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 301 {'server': 'GitHub.com', 'date': 'Mon, 07 Feb 2022 14:16:04 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '161', 'x-oauth-scopes': 'repo', 'x-accepted-oauth-scopes': '', 'github-authentication-token-expiration': '2022-05-08 12:28:34 UTC', 'location': 'https://api.github.com/repos/totycro/stacs/branches/main', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '5000', 'x-ratelimit-remaining': '4966', 'x-ratelimit-reset': '1644244151', 'x-ratelimit-used': '34', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': '4044:ABCD:8C330:8E693:620129A4'} {"message":"Moved Permanently","url":"https://api.github.com/repos/totycro/stacs/branches/main","documentation_url":"https://docs.github.com/v3/#http-redirects"}
| GET https://api.github.com/repos/totycro/stacs/branches/main {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 401 {'server': 'GitHub.com', 'date': 'Mon, 07 Feb 2022 14:16:04 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '80', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '60', 'x-ratelimit-remaining': '59', 'x-ratelimit-reset': '1644246964', 'x-ratelimit-used': '1', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': '4044:ABCD:8C389:8E6FE:620129A4'} {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
```

I'm now working around this by directly fetching `main`, but I guess the redirect from `master` is supposed work as well.
I'm having the same problem.
With curl:
```shell
  TOKEN=`cat gh_token`
  curl -v -H "Authorization: token $TOKEN" https://api.github.com/user
  ```
A portion of the HTML payload returned is:
```json
{
"login": "meangrape",
"id": 9083,
"node_id": "MDQ6VXNlcjkwODM=",
"avatar_url": "https://avatars.githubusercontent.com/u/9083?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/meangrape",
"html_url": "https://github.com/meangrape",
"followers_url": "https://api.github.com/users/meangrape/followers",
"following_url":
"https://api.github.com/users/meangrape/following{/other_user}",
"gists_url": "https://api.github.com/users/meangrape/gists{/gist_id}",...
```

With the following python the error is `BadCredentialsException: 401 {"message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}`

```python
from github import Github
token = open("gh_token").readline().strip()
g = Github(token)
print(g.get_user().get_orgs().totalCount)
```
with a stacktrace
```python
BadCredentialsException                   Traceback (most recent call last)
Input In [26], in <module>
----> 1 print(g.get_user().get_orgs().totalCount)

File ~/Library/Caches/pypoetry/virtualenvs/gh-repo-perms-6skLoHCD-py3.10/lib/python3.10/site-packages/github/PaginatedList.py:149, in PaginatedList.totalCount(self)
    147 # set per_page = 1 so the totalCount is just the number of pages
    148 params.update({"per_page": 1})
--> 149 headers, data = self.__requester.requestJsonAndCheck(
    150     "GET", self.__firstUrl, parameters=params, headers=self.__headers
    151 )
    152 if "link" not in headers:
    153     if data and "total_count" in data:

File ~/Library/Caches/pypoetry/virtualenvs/gh-repo-perms-6skLoHCD-py3.10/lib/python3.10/site-packages/github/Requester.py:353, in Requester.requestJsonAndCheck(self, verb, url, parameters, headers, input)
    352 def requestJsonAndCheck(self, verb, url, parameters=None, headers=None, input=None):
--> 353     return self.__check(
    354         *self.requestJson(
    355             verb, url, parameters, headers, input, self.__customConnection(url)
    356         )
    357     )

File ~/Library/Caches/pypoetry/virtualenvs/gh-repo-perms-6skLoHCD-py3.10/lib/python3.10/site-packages/github/Requester.py:378, in Requester.__check(self, status, responseHeaders, output)
    376 output = self.__structuredFromJson(output)
    377 if status >= 400:
--> 378     raise self.__createException(status, responseHeaders, output)
    379 return responseHeaders, output
```
> ```python
> token = open("gh_token").readline().strip()
> ```

With Python3, `open` does not use `utf-8` encoding as default but whatever `locale.getpreferredencoding()` returns. Maybe the encoding of the file and that default encoding do not match up. But I presume the token is pretty ASCII, so that shouldn't be a problem. Did you inspect the content of `open("gh_token").readline().strip()`?
I think it would be useful to set the log level to DEBUG, then the log would show what ulrllib3 is doing (including retries and redirects):
```python
import logging
logging.root.level = logging.getLevelName('DEBUG')
```
I see this in the log output. I guess the important part is where the request for the master branch returns a `HTTP 301`.
```
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.github.com:443
DEBUG:urllib3.connectionpool:https://api.github.com:443 "GET /repos/totycro/stacs HTTP/1.1" 200 None
DEBUG:github.Requester:GET https://api.github.com/repos/totycro/stacs {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 200 {'server': 'GitHub.com', 'date': 'Mon, 14 Feb 2022 08:24:45 GMT', 'content-type': 'application/json; charset=utf-8', 'transfer-encoding': 'chunked', 'cache-control': 'private, max-age=60, s-maxage=60', 'vary': 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With', 'etag': 'W/"c848b5c0015386e7a6698f5720a185fe6e7fc29d1599bad4553dd8bb87d5b201"', 'last-modified': 'Mon, 07 Feb 2022 14:12:32 GMT', 'x-oauth-scopes': 'repo', 'x-accepted-oauth-scopes': 'repo', 'github-authentication-token-expiration': '2022-05-08 12:28:34 UTC', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '5000', 'x-ratelimit-remaining': '4977', 'x-ratelimit-reset': '1644830467', 'x-ratelimit-used': '23', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'content-encoding': 'gzip', 'x-github-request-id': '4020:48D9:1623731:1689D99:620A11CD'} {"id":456533757,"node_id":"R_kgDOGzYm_Q","name":"stacs","full_name":"totycro/stacs","private":true,"owner":{"login":"totycro","id":357581,"node_id":"MDQ6VXNlcjM1NzU4MQ==","avatar_url":"https://avatars.githubusercontent.com/u/357581?v=4","gravatar_id":"","url":"https://api.github.com/users/totycro","html_url":"https://github.com/totycro","followers_url":"https://api.github.com/users/totycro/followers","following_url":"https://api.github.com/users/totycro/following{/other_user}","gists_url":"https://api.github.com/users/totycro/gists{/gist_id}","starred_url":"https://api.github.com/users/totycro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/totycro/subscriptions","organizations_url":"https://api.github.com/users/totycro/orgs","repos_url":"https://api.github.com/users/totycro/repos","events_url":"https://api.github.com/users/totycro/events{/privacy}","received_events_url":"https://api.github.com/users/totycro/received_events","type":"User","site_admin":false},"html_url":"https://github.com/totycro/stacs","description":null,"fork":false,"url":"https://api.github.com/repos/totycro/stacs","forks_url":"https://api.github.com/repos/totycro/stacs/forks","keys_url":"https://api.github.com/repos/totycro/stacs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/totycro/stacs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/totycro/stacs/teams","hooks_url":"https://api.github.com/repos/totycro/stacs/hooks","issue_events_url":"https://api.github.com/repos/totycro/stacs/issues/events{/number}","events_url":"https://api.github.com/repos/totycro/stacs/events","assignees_url":"https://api.github.com/repos/totycro/stacs/assignees{/user}","branches_url":"https://api.github.com/repos/totycro/stacs/branches{/branch}","tags_url":"https://api.github.com/repos/totycro/stacs/tags","blobs_url":"https://api.github.com/repos/totycro/stacs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/totycro/stacs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/totycro/stacs/git/refs{/sha}","trees_url":"https://api.github.com/repos/totycro/stacs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/totycro/stacs/statuses/{sha}","languages_url":"https://api.github.com/repos/totycro/stacs/languages","stargazers_url":"https://api.github.com/repos/totycro/stacs/stargazers","contributors_url":"https://api.github.com/repos/totycro/stacs/contributors","subscribers_url":"https://api.github.com/repos/totycro/stacs/subscribers","subscription_url":"https://api.github.com/repos/totycro/stacs/subscription","commits_url":"https://api.github.com/repos/totycro/stacs/commits{/sha}","git_commits_url":"https://api.github.com/repos/totycro/stacs/git/commits{/sha}","comments_url":"https://api.github.com/repos/totycro/stacs/comments{/number}","issue_comment_url":"https://api.github.com/repos/totycro/stacs/issues/comments{/number}","contents_url":"https://api.github.com/repos/totycro/stacs/contents/{+path}","compare_url":"https://api.github.com/repos/totycro/stacs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/totycro/stacs/merges","archive_url":"https://api.github.com/repos/totycro/stacs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/totycro/stacs/downloads","issues_url":"https://api.github.com/repos/totycro/stacs/issues{/number}","pulls_url":"https://api.github.com/repos/totycro/stacs/pulls{/number}","milestones_url":"https://api.github.com/repos/totycro/stacs/milestones{/number}","notifications_url":"https://api.github.com/repos/totycro/stacs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/totycro/stacs/labels{/name}","releases_url":"https://api.github.com/repos/totycro/stacs/releases{/id}","deployments_url":"https://api.github.com/repos/totycro/stacs/deployments","created_at":"2022-02-07T14:12:32Z","updated_at":"2022-02-07T14:12:32Z","pushed_at":"2022-02-08T15:29:11Z","git_url":"git://github.com/totycro/stacs.git","ssh_url":"git@github.com:totycro/stacs.git","clone_url":"https://github.com/totycro/stacs.git","svn_url":"https://github.com/totycro/stacs","homepage":null,"size":4,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":1,"license":null,"allow_forking":true,"is_template":false,"topics":[],"visibility":"private","forks":0,"open_issues":1,"watchers":0,"default_branch":"main","permissions":{"admin":true,"maintain":true,"push":true,"triage":true,"pull":true},"temp_clone_token":"AACXJTK4VNAS4LQVI5OD5ELCBIJPS","allow_squash_merge":true,"allow_merge_commit":true,"allow_rebase_merge":true,"allow_auto_merge":false,"delete_branch_on_merge":false,"allow_update_branch":false,"network_count":0,"subscribers_count":1}
DEBUG:urllib3.connectionpool:https://api.github.com:443 "GET /repos/totycro/stacs/branches/master HTTP/1.1" 301 161
DEBUG:github.Requester:GET https://api.github.com/repos/totycro/stacs/branches/master {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 301 {'server': 'GitHub.com', 'date': 'Mon, 14 Feb 2022 08:24:45 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '161', 'x-oauth-scopes': 'repo', 'x-accepted-oauth-scopes': '', 'github-authentication-token-expiration': '2022-05-08 12:28:34 UTC', 'location': 'https://api.github.com/repos/totycro/stacs/branches/main', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '5000', 'x-ratelimit-remaining': '4976', 'x-ratelimit-reset': '1644830467', 'x-ratelimit-used': '24', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': '4020:48D9:1623799:1689E0E:620A11CD'} {"message":"Moved Permanently","url":"https://api.github.com/repos/totycro/stacs/branches/main","documentation_url":"https://docs.github.com/v3/#http-redirects"}
DEBUG:urllib3.connectionpool:https://api.github.com:443 "GET /repos/totycro/stacs/branches/main HTTP/1.1" 401 80
DEBUG:github.Requester:GET https://api.github.com/repos/totycro/stacs/branches/main {'Authorization': 'token (oauth token removed)', 'User-Agent': 'PyGithub/Python'} None ==> 401 {'server': 'GitHub.com', 'date': 'Mon, 14 Feb 2022 08:24:46 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '80', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '60', 'x-ratelimit-remaining': '52', 'x-ratelimit-reset': '1644830537', 'x-ratelimit-used': '8', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': '4020:48D9:16237E4:1689E53:620A11CD'} {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
```
This is so hard to reproduce. Can everyone who observes this issue here please give the following details:

- which PyGithub version is affected?
- do you access a public or private repository?
- please create a bunch of tokens, do you see this error for all of them?
- what are the permissions of those tokens?
- example code that produces the error (if you haven't yet above)

If someone who can reproduce the issue could please do the following:

1. setup a virtual env: `python -m virtualenv venv`
2. activate the virtual env: `source venv/bin/activate`
3. install PyGithub: `pip install PyGithub`
4. edit the `Requester.py`: `nano venv/lib/python3.8/site-packages/github/Requester.py`, replace in `def __log`:
```python
elif requestHeaders["Authorization"].startswith("token"):
  headersForRequest["Authorization"] = "token (oauth token removed)"
```
with
```python
elif requestHeaders["Authorization"].startswith("token"):
  import hashlib
  token = requestHeaders["Authorization"][5:]
  token = hashlib.md5(token.encode('utf-8')).hexdigest()
  headersForRequest["Authorization"] = f"token ({token})"
```
5. run your code with `github.enable_console_debug_logging()`:
```python
import github
github.enable_console_debug_logging()
token = '...'
gh = github.Github(token)
gh.get_repo('totycro/stacs').get_branch('master')
```

Now, instead of
```
GET https://api.github.com/repos/totycro/stacs/branches/master {'Authorization': 'token (oauth token removed)', 'User-Agent': ...
GET https://api.github.com/repos/totycro/stacs/branches/main {'Authorization': 'token (oauth token removed)', 'User-Agent': ...
```
you should see:
```
GET https://api.github.com/repos/totycro/stacs/branches/master {'Authorization': 'token (f1224d3ef4c528fe4b982b5a5a923bac)', 'User-Agent': ...
GET https://api.github.com/repos/totycro/stacs/branches/main {'Authorization': 'token (f1224d3ef4c528fe4b982b5a5a923bac)', 'User-Agent': ...
```

Please verify that the hashes behind `token` are identical in all lines (here `f1224d3ef4c528fe4b982b5a5a923bac`).
I don't have `headersForRequest` I have `requestHeaders` at line 638 so I went ahead and kept `requestHeaders`
```
elif requestHeaders["Authorization"].startswith("token"):
  headersForRequest["Authorization"] = "token (oauth token removed)"
```

```
elif requestHeaders["Authorization"].startswith("token"):
  requestHeaders["Authorization"] = "token (oauth token removed)"
```

My output doesn't consist of 2 lines, just the normal debug log info.
```
GET https://api.github.com/repos/totycro/stacs {'Authorization': 'token (bef1b10214a51913136de8faacd49238)', 'User-Agent': 'PyGithub/Python'} None ==> 401 {'server': 'GitHub.com', 'date': 'Tue, 15 Feb 2022 00:13:47 GMT', 'content-type': 'application/json; charset=utf-8', 'content-length': '80', 'x-github-media-type': 'github.v3; format=json', 'x-ratelimit-limit': '60', 'x-ratelimit-remaining': '14', 'x-ratelimit-reset': '1644884924', 'x-ratelimit-used': '46', 'x-ratelimit-resource': 'core', 'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubdomains; preload', 'x-frame-options': 'deny', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'content-security-policy': "default-src 'none'", 'vary': 'Accept-Encoding, Accept, X-Requested-With', 'x-github-request-id': 'DC1E:7635:53E695F:A5A7027:620AF03B'} {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
```

Hay, I had the same problem just now and I just enabled all the checkboxes when creating the token, so the error stopped appearing
I'd prefer not to give all permissions to a token that doesn't need them. Though I can try adding them one at a time and see if that helps.
> I'd prefer not to give all permissions to a token that doesn't need them. Though I can try adding them one at a time and see if that helps.

Actually, that was not the problem. Then I discovered that every time I made a commit, my token became invalid. This happened because the token was in one of the files in the repository.
Ohh 
I am also facing this error, and am able to consistently recreate it on 1.55 when all of these happen at once:
- Logging level is set to DEBUG
- The URL in question involves a redirect
- A token is used

Here is my script that consistently recreates the issue:

```python
from os import getenv
import logging

from github import Github

logging.basicConfig(
    level="DEBUG",
)

access_token = getenv("GITHUB_TOKEN")

gh = Github(access_token)
github_repo = gh.get_repo("whoopnip/check-if-issue-exists-action")

print(github_repo.name)
```

When running this script with GITHUB_TOKEN=ghp... then it fails. Without a token, it succeeds. Removing debug logging, it succeeds either way. And when switching the url to `nickderobertis/check-if-issue-exists-action` (the url it is redirecting to), then it also succeeds.
When it gives **"message": "Bad credentials",**  There can be only one possible reasons: you token is not valid. Most probably, your are not setting it properly. 

Would suggest you to use below one command:
`g = Github(base_url="https://<github_server_name>/api/v3/", login_or_token="<token_value>")`

then try it.

Hey folks!! I figured it out; at least in my instance. 
For those of you still getting this error, React to this if your org is private.

It looks like it has to do with a minor change with how the python requests library handles auth headers. You can't specify the header manually as there's a special location that needs to be updated.

https://github.com/PyGithub/PyGithub/blob/001970d4a828017f704f6744a5775b4207a6523c/github/Requester.py#L122
For a test I inserted this below that line:
```
        for header in self.headers:
            if str(self.headers[header]).startswith('token'):
                self.session.auth=(tuple(self.headers[header].split(' ')))
```

This is based on how auth is set here for sessions: https://requests.readthedocs.io/en/latest/user/advanced/
and here for non sessions: https://requests.readthedocs.io/en/latest/user/authentication/

I'm not sure why the auth header is overwritten when auth is unset BUT it's got me working right now. Going to see if I can work on a patch

Edit 1:
While I don't have a netrc file; the cause may be related : https://requests.readthedocs.io/en/latest/user/authentication/#netrc-authentication


Edit 2:
This is where it started checking directories OUTside the script for netrc
https://github.com/psf/requests/commit/ba543713d35067866d68b09f644042c0c021a8ba#diff-b4b6ac4e508ec3e142b8b8a3cf33b9ab925e3bdd03dbbe859384760746b1ac76R172

Which time wise matches when this issue started
I had the same issue. The first time when I used my token everything is fine. Then it did not work anymore. Again I created a new token (all permissions checked) and the first time it worked then again not. Later I checked my emails and saw that Github send me a notification, where they automatically revoked my developer token because they found it in a git commit. (I made a test commit with my original token as an input test). Maybe some of you people did the same as me, so the token got revoked.
However, I don't know if this is the only cause for such an error but maybe for some of you.

 @ShiftedMr I checked out your PR and unfortunately, it couldn't solve the above-described scenario, where Github revokes the token automatically.
@efstratios97 My issue was not related to the revoked token. 
I captured the issue my and my teammates were running into.
The issue only raises itself on non public repos as with public repos it doesn't need a valid auth header
For me, the issue was the `~/.netrc` file. Contents in the file were being picked up even when the token is passed to the `Gitthub` constructor.

Solution:
- Try deleting or updating `~/.netrc` accordlingly
Me, too.

I'd been successfully using PyGitHub for a couple hours of programming.  Then, _running the same two-line script that had worked previously_, `github.GithubException.BadCredentialsException` erupted.  `curl ...` with the same **token** works.  `gh ...` with the same **token** now fails as well.

I have no `~/.netrc`.  I haven't changed Python version, **venv**, `pygithub` version, ...  I can see the unaltered **token** going out in the header.
I had the same issue with github api and python requests.
```shell
$ grep 'github.com' ~/.netrc -A 2
machine api.github.com
login ***
password ***
```

Removing the entry fixes the issue.

BasicAuth or a custom authenication class can be used if you have no controll over .netrc file:

```python
import requests

GITHUB_KEY = "ghp_***"

class BearerAuthToken(requests.auth.AuthBase):
    def __call__(self, r):
        r.headers['Authorization'] = f'Bearer {GITHUB_KEY}'
        return r


requests.post(
        "https://api.github.com/graphql",
        json={"query": "query { viewer { login }}"}},
        auth=BearerAuthToken() ,
)
```
I almost spent ~2 hours trying to figure out the issue. As @injaelee said, I have removed the github related entries in .netrc file and i was able to use **ghe_instance.get_user().get_repo().delete()** to delete the repo.
Happened to me when I accidentally committed the token. GitHub immediately revoked it.

Resolving patch:
diff --git a/github/Auth.py b/github/Auth.py
--- a/github/Auth.py
+++ b/github/Auth.py
@@ -23,10 +23,12 @@
 import abc
 import base64
 import time
+from abc import ABC
 from datetime import datetime, timedelta, timezone
 from typing import Dict, Optional, Union
 
 import jwt
+from requests import utils
 
 from github import Consts
 from github.InstallationAuthorization import InstallationAuthorization
@@ -59,7 +61,27 @@ def token(self) -> str:
         """
 
 
-class Login(Auth):
+class HTTPBasicAuth(Auth, abc.ABC):
+    @property
+    @abc.abstractmethod
+    def username(self) -> str:
+        """The username."""
+
+    @property
+    @abc.abstractmethod
+    def password(self) -> str:
+        """The password"""
+
+    @property
+    def token_type(self) -> str:
+        return "Basic"
+
+    @property
+    def token(self) -> str:
+        return base64.b64encode(f"{self.username}:{self.password}".encode()).decode("utf-8").replace("\n", "")
+
+
+class Login(HTTPBasicAuth):
     """
     This class is used to authenticate Requester with login and password.
     """
@@ -78,16 +100,12 @@ def login(self) -> str:
         return self._login
 
     @property
-    def password(self) -> str:
-        return self._password
-
-    @property
-    def token_type(self) -> str:
-        return "Basic"
+    def username(self) -> str:
+        return self.login
 
     @property
-    def token(self) -> str:
-        return base64.b64encode(f"{self.login}:{self.password}".encode()).decode("utf-8").replace("\n", "")
+    def password(self) -> str:
+        return self._password
 
 
 class Token(Auth):
@@ -109,7 +127,7 @@ def token(self) -> str:
         return self._token
 
 
-class JWT(Auth):
+class JWT(Auth, ABC):
     """
     This class is the base class to authenticate with a JSON Web Token (JWT).
     https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app
@@ -328,6 +346,8 @@ def __init__(
         refresh_expires_at: Optional[datetime] = None,
         requester: Optional[Requester] = None,
     ) -> None:
+        super().__init__()
+
         assert isinstance(client_id, str)
         assert len(client_id) > 0
         assert isinstance(client_secret, str)
@@ -421,3 +441,40 @@ def refresh_token(self) -> Optional[str]:
     @property
     def refresh_expires_at(self) -> Optional[datetime]:
         return self._refresh_expires_at
+
+
+class NetrcAuth(HTTPBasicAuth, WithRequester["NetrcAuth"]):
+    """
+    This class is used to authenticate Requester via .netrc.
+    """
+
+    def __init__(self) -> None:
+        super().__init__()
+
+        self._login: Optional[str] = None
+        self._password: Optional[str] = None
+
+    @property
+    def username(self) -> str:
+        return self.login
+
+    @property
+    def login(self) -> str:
+        assert self._login is not None, "Method withRequester(Requester) must be called first"
+        return self._login
+
+    @property
+    def password(self) -> str:
+        assert self._password is not None, "Method withRequester(Requester) must be called first"
+        return self._password
+
+    def withRequester(self, requester: Requester) -> "NetrcAuth":
+        super().withRequester(requester)
+
+        auth = utils.get_netrc_auth(requester.base_url, raise_errors=True)
+        if auth is None:
+            raise RuntimeError(f"Could not get credentials from netrc for host {requester.hostname}")
+
+        self._login, self._password = auth
+
+        return self
diff --git a/github/Requester.py b/github/Requester.py
--- a/github/Requester.py
+++ b/github/Requester.py
@@ -118,6 +118,10 @@ def __init__(
         self.timeout = timeout
         self.verify = kwargs.get("verify", True)
         self.session = requests.Session()
+        # having Session.auth set something other than None disables falling back to .netrc file
+        # https://github.com/psf/requests/blob/d63e94f552ebf77ccf45d97e5863ac46500fa2c7/src/requests/sessions.py#L480-L481
+        # see https://github.com/PyGithub/PyGithub/pull/2703
+        self.session.auth = Requester.noopAuth
 
         if retry is None:
             self.retry = requests.adapters.DEFAULT_RETRIES
@@ -183,6 +187,10 @@ def __init__(
         self.timeout = timeout
         self.verify = kwargs.get("verify", True)
         self.session = requests.Session()
+        # having Session.auth set something other than None disables falling back to .netrc file
+        # https://github.com/psf/requests/blob/d63e94f552ebf77ccf45d97e5863ac46500fa2c7/src/requests/sessions.py#L480-L481
+        # see https://github.com/PyGithub/PyGithub/pull/2703
+        self.session.auth = Requester.noopAuth
 
         if retry is None:
             self.retry = requests.adapters.DEFAULT_RETRIES
@@ -236,6 +244,10 @@ class Requester:
 
     _frameBuffer: List[Any]
 
+    @staticmethod
+    def noopAuth(request: requests.models.PreparedRequest) -> requests.models.PreparedRequest:
+        return request
+
     @classmethod
     def injectConnectionClasses(
         cls,
@@ -409,6 +421,10 @@ def kwargs(self) -> Dict[str, Any]:
     def base_url(self) -> str:
         return self.__base_url
 
+    @property
+    def hostname(self) -> str:
+        return self.__hostname
+
     @property
     def auth(self) -> Optional["Auth"]:
         return self.__auth


Test patch:
diff --git a/tests/Authentication.py b/tests/Authentication.py
--- a/tests/Authentication.py
+++ b/tests/Authentication.py
@@ -26,7 +26,9 @@
 #                                                                              #
 ################################################################################
 
+import os
 from datetime import datetime, timezone
+from tempfile import NamedTemporaryFile
 from unittest import mock
 
 import jwt
@@ -244,6 +246,33 @@ def testAppUserAuthentication(self):
         user = g.get_user()
         self.assertEqual(user.login, "EnricoMi")
 
+    def testNetrcAuth(self):
+        with NamedTemporaryFile("wt", delete=False) as tmp:
+            # write temporary netrc file
+            tmp.write("machine api.github.com\n")
+            tmp.write("login github-user\n")
+            tmp.write("password github-password\n")
+            tmp.close()
+
+            auth = github.Auth.NetrcAuth()
+            with mock.patch.dict(os.environ, {"NETRC": tmp.name}):
+                github.Github(auth=auth)
+
+            self.assertEqual(auth.login, "github-user")
+            self.assertEqual(auth.password, "github-password")
+            self.assertEqual(auth.token, "Z2l0aHViLXVzZXI6Z2l0aHViLXBhc3N3b3Jk")
+            self.assertEqual(auth.token_type, "Basic")
+
+    def testNetrcAuthFails(self):
+        # provide an empty netrc file to make sure this test does not find one
+        with NamedTemporaryFile("wt", delete=False) as tmp:
+            tmp.close()
+            auth = github.Auth.NetrcAuth()
+            with mock.patch.dict(os.environ, {"NETRC": tmp.name}):
+                with self.assertRaises(RuntimeError) as exc:
+                    github.Github(auth=auth)
+                self.assertEqual(exc.exception.args, ("Could not get credentials from netrc for host api.github.com",))
+
     def testCreateJWT(self):
         auth = github.Auth.AppAuth(APP_ID, PRIVATE_KEY)