rustauth_plugins/generic_oauth/
mod.rs1mod account;
4mod config;
5mod discovery;
6mod errors;
7mod provider;
8pub mod providers;
9mod route_http;
10mod route_support;
11mod routes;
12mod user_info;
13
14use rustauth_core::plugin::{AuthPlugin, PluginInitOutput};
15use rustauth_oauth::oauth2::SocialOAuthProvider;
16use std::collections::BTreeSet;
17use std::sync::Arc;
18
19pub const UPSTREAM_PLUGIN_ID: &str = "generic-oauth";
20
21pub use config::{
22 GenericOAuthConfig, GenericOAuthFlow, GenericOAuthGetToken, GenericOAuthGetUserInfo,
23 GenericOAuthMapProfileToUser, GenericOAuthOptions, GenericOAuthOptionsBuilder,
24 GenericOAuthParams, GenericOAuthParamsCallback, GenericOAuthParamsContext,
25 GenericOAuthParamsFuture, GenericOAuthRefreshAccessToken, GenericOAuthRevokeToken,
26 GenericOAuthTokenRequest, GenericOAuthVerifyIdToken,
27};
28pub use errors::{
29 INVALID_OAUTH_CONFIG, INVALID_OAUTH_CONFIGURATION, ISSUER_MISMATCH, ISSUER_MISSING,
30 PROVIDER_CONFIG_NOT_FOUND, PROVIDER_ID_REQUIRED, SESSION_REQUIRED, TOKEN_URL_NOT_FOUND,
31};
32pub use provider::GenericOAuthProvider;
33pub use providers::{
34 auth0, gumroad, hubspot, keycloak, line, microsoft_entra_id, okta, patreon, slack,
35 Auth0Options, BaseOAuthProviderOptions, GumroadOptions, HubSpotOptions, KeycloakOptions,
36 LineOptions, MicrosoftEntraIdOptions, OktaOptions, PatreonOptions, SlackOptions,
37};
38
39#[must_use]
41pub fn generic_oauth(options: GenericOAuthOptions) -> AuthPlugin {
42 let init_options = options.clone();
43 let discovery_cache = discovery::DiscoveryCache::default();
44 let init_discovery_cache = discovery_cache.clone();
45 let plugin = AuthPlugin::new(UPSTREAM_PLUGIN_ID)
46 .with_version(env!("CARGO_PKG_VERSION"))
47 .with_options(options.to_json())
48 .with_error_code(errors::error_code(
49 INVALID_OAUTH_CONFIGURATION,
50 "Invalid OAuth configuration",
51 ))
52 .with_error_code(errors::error_code(
53 TOKEN_URL_NOT_FOUND,
54 "Invalid OAuth configuration. Token URL not found.",
55 ))
56 .with_error_code(errors::error_code(
57 PROVIDER_CONFIG_NOT_FOUND,
58 "No config found for provider",
59 ))
60 .with_error_code(errors::error_code(
61 PROVIDER_ID_REQUIRED,
62 "Provider ID is required",
63 ))
64 .with_error_code(errors::error_code(
65 INVALID_OAUTH_CONFIG,
66 "Invalid OAuth configuration.",
67 ))
68 .with_error_code(errors::error_code(SESSION_REQUIRED, "Session is required"))
69 .with_error_code(errors::error_code(
70 ISSUER_MISMATCH,
71 "OAuth issuer mismatch. The authorization server issuer does not match the expected value (RFC 9207).",
72 ))
73 .with_error_code(errors::error_code(
74 ISSUER_MISSING,
75 "OAuth issuer parameter missing. The authorization server did not include the required iss parameter (RFC 9207).",
76 ))
77 .with_endpoint(routes::sign_in_oauth2_endpoint(
78 options.clone(),
79 discovery_cache.clone(),
80 ))
81 .with_endpoint(routes::oauth2_callback_endpoint(
82 options.clone(),
83 discovery_cache.clone(),
84 ))
85 .with_endpoint(routes::oauth2_link_endpoint(options, discovery_cache));
86
87 plugin.with_init(move |_context| {
88 let mut output = PluginInitOutput::new();
89 let mut seen = BTreeSet::new();
90 for config in &init_options.config {
91 if !seen.insert(config.provider_id.clone()) {
92 continue;
93 }
94 let provider: Arc<dyn SocialOAuthProvider> =
95 Arc::new(GenericOAuthProvider::with_discovery_cache(
96 config.clone(),
97 init_discovery_cache.clone(),
98 ));
99 output = output.social_provider(provider);
100 }
101 Ok(output)
102 })
103}