Skip to content

Commit

Permalink
Adding OAuth2.0 personal clients and refresh routes.
Browse files Browse the repository at this point in the history
  • Loading branch information
holycardboard committed Mar 4, 2024
1 parent 108cf40 commit d5e335f
Show file tree
Hide file tree
Showing 14 changed files with 697 additions and 163 deletions.
43 changes: 42 additions & 1 deletion README.md
Expand Up @@ -104,11 +104,46 @@ var pages = await api.Pages.Pages("2c98fbe9-a63f-47c2-9862-ecc9199610a2");

## Authentication
MangaDex switched to authorization bearer tokens via an OAuth2 flow recently.
In order to access any resources that require an account, you will need to get one of those tokens (you can read more [here](https://api.mangadex.org/docs/authentication/) once they update the docs).
In order to access any resources that require an account, you will need to get one of those tokens (you can read more [here](https://api.mangadex.org/docs/02-authentication/)).
Once you have a bearer token, you can either add it at an API level or for a specific request, you can also create your own token service provider.

> Note: You can see an example of how to fetch bearer tokens for the new OAuth2 flow in the `src/MangaDexSharp.OAuthLocal.Web` project.
### OAuth2 Personal Clients:
You can use the OAuth2 Personal client to fetch the session token like so:

```csharp
var api = MangaDex.Create();

//You can get these from https://mangadex.org/settings under the "API Clients" tab.
string clientId = "<client-id>";
string clientSecret = "<client-secret>";

//These are your mangadex.org account credentials
string username = "<username>";
string password = "<password>";

//Request the tokens from the authorization service
var auth = await api.Auth.Personal(clientId, clientSecret, username, password);
var accessToken = auth.AccessToken;
var refreshToken = auth.RefreshToken;

var me = await api.User.Me(accessToken);

//Or you can create an authenticated api
var authedApi = MangaDex.Create(accessToken);
var me = await authedApi.User.Me();

//You can also refresh the token like so:
var refreshed = await api.Auth.Refresh(refreshToken, clientId, clientSecret);
var me = await api.User.Me(refreshed.AccessToken);
```

### OAuth2 Public Clients:
These are not implemented yet on MangaDex, this library will be updated when they are.

You can read more about them [here](https://api.mangadex.org/docs/02-authentication/public-clients/)

### Legacy Authentication method:
You can use the legacy login method to fetch the session token like so:

Expand Down Expand Up @@ -228,4 +263,10 @@ For the last option, if you want to change the [configuration keys](https://gith
ConfigurationCredentialsService.TokenPath = "SomeOther:Path:ToThe:Token";
ConfigurationCredentialsService.UserAgentPath = "SomeOther:Path:ToThe:UserAgent";
ConfigurationCredentialsService.ApiPath = "SomeOther:Path:ToThe:ApiUrl";
ConfigurationCredentialsService.AuthPath = "SomeOther:Path:ToThe:AuthUrl";
ConfigurationCredentialsService.ClientIdPath = "SomeOther:Path:ToThe:ClientId";
ConfigurationCredentialsService.ClientSecretPath = "SomeOther:Path:ToThe:ClientSecret";
ConfigurationCredentialsService.UsernamePath = "SomeOther:Path:ToThe:Username";
ConfigurationCredentialsService.PasswordPath = "SomeOther:Path:ToThe:Password";
ConfigurationCredentialsService.UserAgentPath = "SomeOther:Path:ToThe:UserAgent";
```
18 changes: 15 additions & 3 deletions src/MangaDexSharp.Cli/Program.cs
Expand Up @@ -14,6 +14,7 @@
});
}, throwOnError: true);


var chapter = await api.Chapter.Get("158f54ed-9983-406d-b882-208858288874");
Console.WriteLine("Chapter: " + chapter.Data.Attributes.Title);

Expand Down Expand Up @@ -42,10 +43,21 @@ await foreach(var chap in allChaps)
var manga = await api.Manga.Get("fc0a7b86-992e-4126-b30f-ca04811979bf");
Console.WriteLine($"Title: {manga.Data.Attributes.Title.First().Value}");

//Example of using the old login method to fetch the current user's token
string username = "",
password = "";

//Example of using the personal client auth method
string clientId = "Your client ID from https://mangadex.org/settings",
clientSecret = "Your client secret from https://mangadex.org/settings";

string username = "Your account username for https://mangadex.org",
password = "Your account password for https://mangadex.org";

var auth = await api.Auth.Personal(clientId, clientSecret, username, password);
Console.WriteLine("Token: " + auth.AccessToken);

var refresh = await api.Auth.Refresh(auth.RefreshToken, clientId, clientSecret);
Console.WriteLine("Refreshed Token: " + refresh.AccessToken);

//Example of using the old login method to fetch the current user's token
var result = await api.User.Login(username, password); //While it says the method is obsolete, it will still work (for now)
var token = result.Data.Session;

Expand Down
10 changes: 10 additions & 0 deletions src/MangaDexSharp.OAuthLocal.Web/WebCredentialService.cs
Expand Up @@ -13,6 +13,16 @@ public WebCredentialService(IHttpContextAccessor context)

public string UserAgent => Extensions.API_USER_AGENT;

public string AuthUrl => Extensions.AUTH_DEV_URL;

public string? ClientId => null;

public string? ClientSecret => null;

public string? Username => null;

public string? Password => null;

public bool ThrowOnError => false;

public Task<string?> GetToken()
Expand Down
118 changes: 118 additions & 0 deletions src/MangaDexSharp/Credentialing/ConfigurationCredentialsService.cs
@@ -0,0 +1,118 @@
namespace MangaDexSharp;


/// <summary>
/// Represents a provider that fetches the <see cref="ICredentialsService"/> from the configuration
/// </summary>
public class ConfigurationCredentialsService : ICredentialsService
{
private readonly IConfiguration _config;

/// <summary>
/// Where to fetch the API token from in the config file
/// </summary>
public static string TokenPath { get; set; } = "Mangadex:Token";

/// <summary>
/// Where to fetch the API url from in the config file
/// </summary>
public static string ApiPath { get; set; } = "Mangadex:ApiUrl";

/// <summary>
/// Where to fetch the Auth URL service from in the config file
/// </summary>
public static string AuthPath { get; set; } = "Mangadex:AuthUrl";

/// <summary>
/// Where to fetch the client ID from in the config file
/// </summary>
public static string ClientIdPath { get; set; } = "Mangadex:ClientId";

/// <summary>
/// Where to fetch the client secret from in the config file
/// </summary>
public static string ClientSecretPath { get; set; } = "Mangadex:ClientSecret";

/// <summary>
/// Where to fetch the username (for the password grant OAuth2 requests) from in the config file
/// </summary>
public static string UsernamePath { get; set; } = "Mangadex:Username";

/// <summary>
/// Where to fetch the password (for the password grant OAuth2 requests) from in the config file
/// </summary>
public static string PasswordPath { get; set; } = "Mangadex:Password";

/// <summary>
/// Where to fetch the User-Agent header from in the config file
/// </summary>
public static string UserAgentPath { get; set; } = "Mangadex:UserAgent";

/// <summary>
/// Where to fetch the ThrowOnError flag from in the config file
/// </summary>
public static string ErrorThrownPath { get; set; } = "Mangadex:ThrowOnError";

/// <summary>
/// The authentication token from the configuration file
/// </summary>
public string? Token => _config[TokenPath];

/// <summary>
/// The API url from the configuration file
/// </summary>
public string ApiUrl => _config[ApiPath] ?? API_ROOT;

/// <summary>
/// The Auth URL service for MangaDex
/// </summary>
public string AuthUrl => _config[AuthPath] ?? AUTH_URL;

/// <summary>
/// The User-Agent header to send with requests
/// </summary>
public string UserAgent => _config[UserAgentPath] ?? API_USER_AGENT;

/// <summary>
/// The client ID for the authorization endpoint
/// </summary>
public string? ClientId => _config[ClientIdPath];

/// <summary>
/// The client secret for the authorization endpoint
/// </summary>
public string? ClientSecret => _config[ClientSecretPath];

/// <summary>
/// The username for password grant requests
/// </summary>
public string? Username => _config[UsernamePath];

/// <summary>
/// The password for password grant requests
/// </summary>
public string? Password => _config[PasswordPath];

/// <summary>
/// Whether or not to throw an exception if the API returns an error
/// </summary>
public bool ThrowOnError => _config[ErrorThrownPath] == "true";

/// <summary>
/// Represents a provider that fetches the <see cref="ICredentialsService"/> from the configuration
/// </summary>
/// <param name="config">The <see cref="IConfiguration"/> object to fetch the variables from</param>
public ConfigurationCredentialsService(IConfiguration config)
{
_config = config;
}

/// <summary>
/// Fetches the user's authentication token from the config file
/// </summary>
/// <returns>The user's authentication token</returns>
public Task<string?> GetToken()
{
return Task.FromResult(Token);
}
}
95 changes: 95 additions & 0 deletions src/MangaDexSharp/Credentialing/HardCodedCredentialsService.cs
@@ -0,0 +1,95 @@
namespace MangaDexSharp;

/// <summary>
/// Represents a provider that stores the credentials in-memory
/// </summary>
public class HardCodedCredentialsService : ICredentialsService
{
/// <summary>
/// The user's authentication token
/// </summary>
public string? Token { get; set; }

/// <summary>
/// The API url
/// </summary>
public string ApiUrl { get; set; }

/// <summary>
/// The Auth URL service for MangaDex
/// </summary>
public string AuthUrl { get; set; }

/// <summary>
/// The client ID for the authorization endpoint
/// </summary>
public string? ClientId { get; set; }

/// <summary>
/// The client secret for the authorization endpoint
/// </summary>
public string? ClientSecret { get; set; }

/// <summary>
/// The User-Agent header to send with requests
/// </summary>
public string UserAgent { get; set; }

/// <summary>
/// Whether or not to throw an exception if the API returns an error
/// </summary>
public bool ThrowOnError { get; set; }

/// <summary>
/// The username for the password grant OAuth2 requests
/// </summary>
public string? Username { get; set; }

/// <summary>
/// The password for the password grant OAuth2 requests
/// </summary>
public string? Password { get; set; }

/// <summary>
/// Represents a provider that stores the credentials in-memory
/// </summary>
/// <param name="token">The user's authentication token</param>
/// <param name="apiUrl">The API url</param>
/// <param name="userAgent">The User-Agent header to send with requests</param>
/// <param name="throwOnError">Whether or not to throw an exception if the API returns an error</param>
/// <param name="authUrl">The Auth URL service for MangaDex</param>
/// <param name="clientId">The client ID for the authorization endpoint</param>
/// <param name="clientSecret">The client secret for the authorization endpoint</param>
/// <param name="username">The username for the password grant OAuth2 requests</param>
/// <param name="password">The password for the password grant OAuth2 requests</param>
public HardCodedCredentialsService(
string? token = null,
string? apiUrl = null,
string? userAgent = null,
bool throwOnError = false,
string? authUrl = null,
string? clientId = null,
string? clientSecret = null,
string? username = null,
string? password = null)
{
UserAgent = userAgent ?? API_USER_AGENT;
Token = token;
ApiUrl = apiUrl ?? API_ROOT;
AuthUrl = authUrl ?? AUTH_URL;
ClientId = clientId;
ClientSecret = clientSecret;
Username = username;
Password = password;
ThrowOnError = throwOnError;
}

/// <summary>
/// Returns the user's API token from in memory
/// </summary>
/// <returns>The user's authentication token</returns>
public Task<string?> GetToken()
{
return Task.FromResult(Token);
}
}
53 changes: 53 additions & 0 deletions src/MangaDexSharp/Credentialing/ICredentialsService.cs
@@ -0,0 +1,53 @@
namespace MangaDexSharp;

/// <summary>
/// A service that provides a method for fetching the API token and URL data
/// </summary>
public interface ICredentialsService
{
/// <summary>
/// The URL for the MangaDex API
/// </summary>
string ApiUrl { get; }

/// <summary>
/// The Auth URL service for MangaDex
/// </summary>
string AuthUrl { get; }

/// <summary>
/// The User-Agent header to send with requests
/// </summary>
string UserAgent { get; }

/// <summary>
/// The client ID for the authorization endpoint
/// </summary>
string? ClientId { get; }

/// <summary>
/// The client secret for the authorization endpoint
/// </summary>
string? ClientSecret { get; }

/// <summary>
/// The username for password grant requests
/// </summary>
string? Username { get; }

/// <summary>
/// The password for password grant requests
/// </summary>
string? Password { get; }

/// <summary>
/// How to fetch the user's authentication token
/// </summary>
/// <returns>The user's authentication token</returns>
Task<string?> GetToken();

/// <summary>
/// Whether or not to throw an exception if the API returns an error
/// </summary>
bool ThrowOnError { get; }
}

0 comments on commit d5e335f

Please sign in to comment.