Metadata-Version: 2.4
Name: kpick-oauth
Version: 0.2.0
Summary: A simple OAuth client library for Google, Kakao, Naver, and GitHub.
Author-email: chuchangman <chuchangman@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/chuchangman/kpick-oauth
Project-URL: Repository, https://github.com/chuchangman/kpick-oauth
Project-URL: Issues, https://github.com/chuchangman/kpick-oauth/issues
Keywords: oauth,kakao,naver,google,github,authentication
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.32.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: wheel; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# kpick-oauth

A small Python OAuth helper library for Kakao, Naver, Google, and GitHub.

## Installation

```bash
pip install kpick-oauth
```

## Usage

```python
from kpick_oauth import (
    OAuthClient,
    OAuthTokenError,
    OAuthUserInfoError,
    generate_state,
    verify_state,
)

client = OAuthClient(
    provider="github",
    client_id="your-client-id",
    client_secret="your-client-secret",
)

# Create a state value before redirecting the user.
# Bind it to the user's session/cookie so the callback can recover it.
state = generate_state()

# state is required. get_authorize_url() raises ValueError if it is empty.
authorize_url = client.get_authorize_url(
    redirect_uri="https://example.com/oauth/callback",
    state=state,
    scope="read:user user:email",
)

# Redirect the user's browser to authorize_url.

# In your callback route, compare the stored state with the returned state.
if not verify_state(stored_state, returned_state):
    raise ValueError("Invalid OAuth state")

try:
    token = client.fetch_token(
        code="authorization-code",
        redirect_uri="https://example.com/oauth/callback",
    )
    user = client.fetch_user(token["access_token"])
except OAuthTokenError:
    # Provider returned an error payload (e.g. invalid_grant) or no access_token.
    raise
except OAuthUserInfoError:
    # Provider returned an error payload from the userinfo endpoint.
    raise

# user is an OAuthUser, not your application's main user account.
# Use (user.provider, user.provider_id) as the OAuth account key.
# Only trust user.email when user.email_verified is True.
app_user = link_or_create_account_by_provider_id(user.provider, user.provider_id)

if user.email_verified is True and user.email:
    app_user.email = user.email
```

## Security Notes

- Keep `client_secret` on your server. Do not put it in browser JavaScript.
- Do not put access tokens in URLs.
- Do not print or log tokens.
- Use `generate_state()` before redirecting to the provider.
- Use `verify_state()` in your callback before exchanging the code for a token.
- `get_authorize_url()` requires a non-empty `state`.
- Store provider login records by `provider` and `provider_id`.
- Do not use `email` as the OAuth account key.
- Kakao and Google emails are returned only when the provider marks them verified.
- Naver and GitHub emails are marked verified when returned by the provider.
- GitHub noreply emails are treated as unavailable and returned as `None`.
- Check `user.email_verified` before trusting an email for account recovery or linking.
- Do not log `OAuthUser.raw`; it can contain personal information.
- Token and userinfo provider error payloads raise `OAuthTokenError` / `OAuthUserInfoError`. This includes Kakao's negative `code` and Naver's non-`"00"` `resultcode`.

## Supported Providers

- Kakao
- Naver
- Google
- GitHub

## Development

```bash
pip install -e ".[dev]"
pytest
```
