diff --git a/ghjk.deno.lock b/ghjk.deno.lock index ba3f134d77..30d24beaed 100644 --- a/ghjk.deno.lock +++ b/ghjk.deno.lock @@ -692,6 +692,10 @@ "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/deps/cli.ts": "4eacc555cf80686b487e7502db63a4cfbc2060a7b847d15b14cf1cc008a3b65c", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/deps/common.ts": "6f9a23942fa235ecc0cf4846e3d9491dd0e19c594cb7ef181cf17fbff990f358", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/deps/ports.ts": "dcad4dada6a66297ebbda9828473a4a1da3a0ea9f1dd9b5a9b06afd9b10c7ddc", + "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/host/deno.ts": "b4e7867d96bf9fa4db77ec712aac3ec3847bb4edcd44ad572ce60c8838c3b4a9", + "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/host/mod.ts": "30461f16cbb89c80b2562edcfc1bd5a4b94d52bdb7a5a0f0ec3bc2bef413635a", + "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/host/types.ts": "b9be5124f203f95407fe20bb03f45e2132d835f5edb1afeefaeb4345a72382e4", + "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/main.ts": "b35b789f32f1ecfe2ef81521041499dd30a54a86c0564c400b1eb3ab11e96f17", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/mod.ts": "73df1409291986c86df474f37d279518fc099d34be488631d749ac0f80a2c60e", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/mod.ts": "6a528ba3bebfabb1278f86648099066f666a8cf9e74f3f5b047b57053292e7b2", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/ports/ambient.ts": "25623410c535e2bfaf51fca1e582e7325a00a7690d5b5e763a12be9407f619cf", @@ -708,6 +712,7 @@ "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/tasks/deno.ts": "f988a4d1062364b99272087fa0c7d54e699944ead3790c5b83140577bda089de", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/tasks/mod.ts": "3ab02022f3dd66ed1430715f0c23bc053a1cfcd74fc6d2932b348539b2a63e15", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/tasks/types.ts": "92b61837b5e983a1e74739b2e4a15973521af79367d99e867b36ffedeb0f9039", + "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/modules/types.ts": "1bc64f8df44ad2999c1484b5f338c67068ff79cdf9449da49fb86b5c2c9d353f", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/port.ts": "7dbc715c609d6615531aa5beab75ca28655a23bc3b49ef98f924777e47aca86a", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/ports/act.ts": "a42bcd3e68ee476ccf30d99cdb7bc487cfad00d2e8fbf152e5776a32ccb29c76", "https://raw.githubusercontent.com/metatypedev/ghjk/dc9b402/ports/asdf.ts": "70033efcd24b7d2b836e1bc36754597de69a4a1771174d3fbc2fdda6c45f5d0a", diff --git a/ghjk.lock b/ghjk.lock index 8803c4a8b0..013f91f31f 100644 --- a/ghjk.lock +++ b/ghjk.lock @@ -2621,19 +2621,19 @@ "portRef": "node_org@0.1.0", "instId": "node_org@0.1.0+88ba357c" }, - "asdf@0.1.0+65d907b4": { - "instId": "asdf@0.1.0+65d907b4", + "asdf@0.1.0+6974a914": { + "instId": "asdf@0.1.0+6974a914", "portRef": "asdf@0.1.0", "config": { "version": "3.28.0-rc6", "depConfigs": { "curl_aa": { - "version": "8.2.1", + "version": "8.5.0", "depConfigs": {}, "portRef": "curl_aa@0.1.0" }, "git_aa": { - "version": "2.40.1", + "version": "2.43.0", "depConfigs": {}, "portRef": "git_aa@0.1.0" }, @@ -2641,7 +2641,7 @@ "version": "d631481e96", "depConfigs": { "git_aa": { - "version": "2.40.1", + "version": "2.43.0", "depConfigs": {}, "portRef": "git_aa@0.1.0" } @@ -2661,12 +2661,12 @@ "installType": "version" } }, - "asdf_plugin_git@0.1.0+4f72e64a": { + "asdf_plugin_git@0.1.0+9387764b": { "config": { "version": "d631481e96", "depConfigs": { "git_aa": { - "version": "2.40.1", + "version": "2.43.0", "depConfigs": {}, "portRef": "git_aa@0.1.0" } @@ -2675,25 +2675,25 @@ "pluginRepo": "https://github.com/asdf-community/asdf-cmake" }, "portRef": "asdf_plugin_git@0.1.0", - "instId": "asdf_plugin_git@0.1.0+4f72e64a" + "instId": "asdf_plugin_git@0.1.0+9387764b" }, - "git_aa@0.1.0+46876952": { + "git_aa@0.1.0+e1dc78fc": { "config": { - "version": "2.40.1", + "version": "2.43.0", "depConfigs": {}, "portRef": "git_aa@0.1.0" }, "portRef": "git_aa@0.1.0", - "instId": "git_aa@0.1.0+46876952" + "instId": "git_aa@0.1.0+e1dc78fc" }, - "curl_aa@0.1.0+111de4ca": { + "curl_aa@0.1.0+5f2adea7": { "config": { - "version": "8.2.1", + "version": "8.5.0", "depConfigs": {}, "portRef": "curl_aa@0.1.0" }, "portRef": "curl_aa@0.1.0", - "instId": "curl_aa@0.1.0+111de4ca" + "instId": "curl_aa@0.1.0+5f2adea7" }, "protoc_ghrel@0.1.0+95df0014": { "instId": "protoc_ghrel@0.1.0+95df0014", @@ -2790,7 +2790,7 @@ "wasm_opt_cbinst@0.1.0+66136275", "cargo_insta_cbinst@0.1.0+c37139e3", "protoc_ghrel@0.1.0+95df0014", - "asdf@0.1.0+65d907b4", + "asdf@0.1.0+6974a914", "jco_npm@0.1.0+474f388b", "node_org@0.1.0+3d080bd3", "mold_ghrel@0.1.0+76e364ed", @@ -2804,8 +2804,8 @@ "tar_aa@0.1.0+d9cbe4e3", "act_ghrel@0.1.0+e9b6de66", "zstd_aa@0.1.0+993fa832", - "git_aa@0.1.0+46876952", - "curl_aa@0.1.0+111de4ca", + "git_aa@0.1.0+e1dc78fc", + "curl_aa@0.1.0+5f2adea7", "protoc_ghrel@0.1.0+95df0014", "cargo_binstall_ghrel@0.1.0+dfb618ea", "pnpm_ghrel@0.1.0+99af1bf3" @@ -2830,15 +2830,15 @@ "node_org@0.1.0+88ba357c": [ "jco_npm@0.1.0+474f388b" ], - "curl_aa@0.1.0+111de4ca": [ - "asdf@0.1.0+65d907b4" + "curl_aa@0.1.0+5f2adea7": [ + "asdf@0.1.0+6974a914" ], - "git_aa@0.1.0+46876952": [ - "asdf@0.1.0+65d907b4", - "asdf_plugin_git@0.1.0+4f72e64a" + "git_aa@0.1.0+e1dc78fc": [ + "asdf@0.1.0+6974a914", + "asdf_plugin_git@0.1.0+9387764b" ], - "asdf_plugin_git@0.1.0+4f72e64a": [ - "asdf@0.1.0+65d907b4" + "asdf_plugin_git@0.1.0+9387764b": [ + "asdf@0.1.0+6974a914" ], "cargo_binstall_ghrel@0.1.0+dfb618ea": [ "cargo_insta_cbinst@0.1.0+c37139e3", @@ -2899,23 +2899,23 @@ "tar_aa" ] ], - "asdf@0.1.0+65d907b4": [ + "asdf@0.1.0+6974a914": [ [ - "curl_aa@0.1.0+111de4ca", + "curl_aa@0.1.0+5f2adea7", "curl_aa" ], [ - "git_aa@0.1.0+46876952", + "git_aa@0.1.0+e1dc78fc", "git_aa" ], [ - "asdf_plugin_git@0.1.0+4f72e64a", + "asdf_plugin_git@0.1.0+9387764b", "asdf_plugin_git" ] ], - "asdf_plugin_git@0.1.0+4f72e64a": [ + "asdf_plugin_git@0.1.0+9387764b": [ [ - "git_aa@0.1.0+46876952", + "git_aa@0.1.0+e1dc78fc", "git_aa" ] ], diff --git a/typegraph/core/src/utils/mod.rs b/typegraph/core/src/utils/mod.rs index 71e62d03e7..208b27e249 100644 --- a/typegraph/core/src/utils/mod.rs +++ b/typegraph/core/src/utils/mod.rs @@ -8,14 +8,14 @@ use serde_json::json; use crate::errors::Result; use crate::global_store::Store; -use crate::runtimes::{DenoMaterializer, Materializer}; -use crate::t::TypeBuilder; use crate::types::TypeId; use crate::wit::core::{Guest, TypeBase, TypeId as CoreTypeId, TypeStruct}; -use crate::wit::runtimes::MaterializerDenoFunc; use crate::wit::utils::Auth as WitAuth; -use crate::{t, Lib}; +use crate::Lib; +use self::oauth2::std::{named_provider, Oauth2Builder}; + +mod oauth2; pub mod reduce; fn find_missing_props( @@ -79,17 +79,6 @@ impl TryFrom> for String { } } -macro_rules! gen_profiler_func { - ($inp:expr, $out: expr, $profiler:expr) => {{ - let deno_mat = DenoMaterializer::Inline(MaterializerDenoFunc { - code: $profiler.to_string(), - secrets: vec![], - }); - let mat = Materializer::deno(deno_mat, crate::wit::runtimes::Effect::Read); - t::func($inp, $out, Store::register_materializer(mat)) - }}; -} - impl crate::wit::utils::Guest for crate::Lib { fn gen_reduceb( supertype_id: CoreTypeId, @@ -184,218 +173,32 @@ impl crate::wit::utils::Guest for crate::Lib { } fn oauth2(service_name: String, scopes: String) -> Result { - let (name, scopes) = (service_name.as_ref(), scopes.as_ref()); - match name { - "digitalocean" => { - let mut account = t::struct_(); - let inp = t::struct_() - .propx("account", account.propx("uuid", t::string())?)? - .build()?; - let out = t::struct_().propx("id", t::integer())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.account.uuid})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://cloud.digitalocean.com/v1/oauth/authorize", - access_url: "https://cloud.digitalocean.com/v1/oauth/token", - // https://docs.digitalocean.com/reference/api/api-reference/#operation/account_get - scopes, - profile_url: Some("https://api.digitalocean.com/v2/account"), - profiler: Some(func), - }) - } - "discord" => { - let inp = t::struct_().propx("id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://discord.com/api/oauth2/authorize", - access_url: "https://discord.com/api/oauth2/token", - // https://discord.com/developers/docs/resources/user - scopes, - profile_url: Some("https://discord.com/api/users/@me"), - profiler: Some(func), - }) - } - "dropbox" => { - let inp = t::struct_().propx("account_id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.account_id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://www.dropbox.com/oauth2/authorize", - access_url: "https://api.dropboxapi.com/oauth2/token", - // https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account - scopes, - profile_url: Some("https://api.dropboxapi.com/2/users/get_current_account"), - profiler: Some(func), - }) - } - "facebook" => { - let inp = t::struct_().propx("id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://www.facebook.com/v16.0/dialog/oauth", - access_url: "https://graph.facebook.com/v16.0/oauth/access_token", - // https://developers.facebook.com/docs/graph-api/overview#me - // https://developers.facebook.com/docs/graph-api/reference/user/ - scopes, - profile_url: Some("https://graph.facebook.com/me"), - profiler: Some(func), - }) - } - "github" => { - let inp = t::struct_().propx("id", t::integer())?.build()?; - let out = t::struct_().propx("id", t::integer())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://github.com/login/oauth/authorize", - access_url: "https://github.com/login/oauth/access_token", - // https://docs.github.com/en/rest/reference/users?apiVersion=2022-11-28#get-the-authenticated-user - scopes, - profile_url: Some("https://api.github.com/user"), - profiler: Some(func), - }) - } - "gitlab" => { - let inp = t::struct_().propx("id", t::integer())?.build()?; - let out = t::struct_().propx("id", t::integer())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://gitlab.com/oauth/authorize", - access_url: "https://gitlab.com/oauth/token", - // https://docs.gitlab.com/ee/api/users.html#list-current-user - scopes, - profile_url: Some("https://gitlab.com/api/v3/user"), - profiler: Some(func), - }) - } - "google" => { - let inp = t::struct_().propx("localId", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.localId})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://accounts.google.com/o/oauth2/v2/auth", - access_url: "https://oauth2.googleapis.com/token", - // https://cloud.google.com/identity-platform/docs/reference/rest/v1/UserInfo - scopes, - profile_url: Some("https://openidconnect.googleapis.com/v1/userinfo"), - profiler: Some(func), - }) - } - "instagram" => { - let inp = t::struct_().propx("id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://api.instagram.com/oauth/authorize", - access_url: "https://api.instagram.com/oauth/access_token", - // https://developers.facebook.com/docs/instagram-basic-display-api/reference/me - // https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#reading - scopes, - profile_url: Some("https://graph.instagram.com/me"), - profiler: Some(func), - }) - } - "linkedin" => { - let inp = t::struct_().propx("id", t::integer())?.build()?; - let out = t::struct_().propx("id", t::integer())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://www.linkedin.com/oauth/v2/authorization", - access_url: "https://www.linkedin.com/oauth/v2/accessToken", - // https://learn.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api#retrieve-current-members-profile - scopes, - profile_url: Some("https://api.linkedin.com/v2/me"), - profiler: Some(func), - }) - } - "microsoft" => { - let inp = t::struct_().propx("id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", - access_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token", - // https://learn.microsoft.com/en-us//javascript/api/@microsoft/teams-js/app.userinfo?view=msteams-client-js-latest - scopes, - profile_url: Some("https://graph.microsoft.com/oidc/userinfo"), - profiler: Some(func), - }) - } - "reddit" => { - let inp = t::struct_() - .propx("id", t::eitherx!(t::integer(), t::string()))? - .build()?; - let out = t::struct_() - .propx("id", t::eitherx!(t::integer(), t::string()))? - .build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://www.reddit.com/api/v1/authorize", - access_url: "https://www.reddit.com/api/v1/access_token", - // https://www.reddit.com/dev/api/#GET_api_v1_me - scopes, - profile_url: Some("https://oauth.reddit.com/api/v1/me"), - profiler: Some(func), - }) - } - "slack" => { - let inp = t::struct_().propx("user_id", t::string())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.user_id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://slack.com/oauth/v2/authorize", - access_url: "https://slack.com/api/oauth.v2.access", - // https://api.slack.com/methods/auth.test - scopes, - profile_url: Some("https://slack.com/api/auth.test"), - profiler: Some(func), - }) - } - "stackexchange" => { - let inp = t::struct_().propx("account_id", t::integer())?.build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: `${p.account_id}`})")?; - Ok(Oauth2Params { - name: "stackexchange", - authorize_url: "https://stackoverflow.com/oauth", - access_url: "https://stackoverflow.com/oauth/access_token/json", - // https://api.stackexchange.com/docs/me - scopes, - profile_url: Some("https://api.stackexchange.com/2.3/me"), - profiler: Some(func), - }) - } - "twitter" => { - let mut data = t::struct_(); - let inp = t::struct_() - .propx("data", data.propx("id", t::string())?)? - .build()?; - let out = t::struct_().propx("id", t::string())?.build()?; - let func = gen_profiler_func!(inp, out, "(p) => ({id: p.data.id})")?; - Ok(Oauth2Params { - name, - authorize_url: "https://twitter.com/i/oauth2/authorize", - access_url: "https://api.twitter.com/2/oauth2/token", - // https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me - scopes, - profile_url: Some("https://api.twitter.com/2/users/me"), - profiler: Some(func), - }) - } - _ => Err(format!("service named {:?} not supported", name)), - }? - .try_into() + Oauth2Builder::new(scopes).build(named_provider(&service_name)?) + } + + fn oauth2_without_profiler(service_name: String, scopes: String) -> Result { + Oauth2Builder::new(scopes) + .no_profiler() + .build(named_provider(&service_name)?) + } + + fn oauth2_with_extended_profiler( + service_name: String, + scopes: String, + extension: String, + ) -> Result { + Oauth2Builder::new(scopes) + .with_extended_profiler(extension) + .build(named_provider(&service_name)?) + } + + fn oauth2_with_custom_profiler( + service_name: String, + scopes: String, + profiler: CoreTypeId, + ) -> Result { + Oauth2Builder::new(scopes) + .with_profiler(profiler.into()) + .build(named_provider(&service_name)?) } } diff --git a/typegraph/core/src/utils/oauth2/mod.rs b/typegraph/core/src/utils/oauth2/mod.rs new file mode 100644 index 0000000000..2caa5b527c --- /dev/null +++ b/typegraph/core/src/utils/oauth2/mod.rs @@ -0,0 +1,32 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +pub mod std; + +use crate::{ + global_store::Store, + runtimes::{DenoMaterializer, Materializer}, + t, + types::TypeId, +}; + +pub use crate::wit::core::Error as TgError; + +pub struct OAuth2Profiler { + pub input: TypeId, + pub output: TypeId, + pub js_code: String, +} + +impl TryInto for OAuth2Profiler { + type Error = TgError; + + fn try_into(self) -> Result { + let deno_mat = DenoMaterializer::Inline(crate::wit::runtimes::MaterializerDenoFunc { + code: self.js_code, + secrets: vec![], + }); + let mat = Materializer::deno(deno_mat, crate::wit::runtimes::Effect::Read); + t::func(self.input, self.output, Store::register_materializer(mat)) + } +} diff --git a/typegraph/core/src/utils/oauth2/std.rs b/typegraph/core/src/utils/oauth2/std.rs new file mode 100644 index 0000000000..7628a47bbc --- /dev/null +++ b/typegraph/core/src/utils/oauth2/std.rs @@ -0,0 +1,503 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +use crate::errors::Result; +use crate::t::{self, TypeBuilder}; +use crate::types::TypeId; +use crate::utils::Oauth2Params; + +use super::OAuth2Profiler; + +pub struct StaticParams { + name: &'static str, + authorize_url: &'static str, + access_url: &'static str, + profile_url: Option<&'static str>, +} + +pub trait StdOauth2Provider { + fn get_static_params(&self) -> StaticParams; + fn default_profiler(&self) -> Result; + + fn extended_profiler(&self, _extension: &str) -> Result { + self.default_profiler() + } +} + +enum ProfilerSource { + None, + Default, + Extended(String), + Provided(TypeId), +} + +pub struct Oauth2Builder { + scopes: String, + profiler_source: ProfilerSource, +} + +impl Oauth2Builder { + pub fn new(scopes: String) -> Self { + Self { + scopes, + profiler_source: ProfilerSource::Default, + } + } + + pub fn no_profiler(mut self) -> Self { + self.profiler_source = ProfilerSource::None; + self + } + + pub fn with_extended_profiler(mut self, extension: String) -> Self { + self.profiler_source = ProfilerSource::Extended(extension); + self + } + + pub fn with_profiler(mut self, profiler: TypeId) -> Self { + self.profiler_source = ProfilerSource::Provided(profiler); + self + } + + pub fn build(self, provider: Box) -> Result { + let StaticParams { + name, + authorize_url, + access_url, + profile_url, + } = provider.get_static_params(); + + let profiler = match self.profiler_source { + ProfilerSource::None => None, + ProfilerSource::Default => Some(provider.default_profiler()?.try_into()?), + ProfilerSource::Extended(extension) => { + Some(provider.extended_profiler(&extension)?.try_into()?) + } + ProfilerSource::Provided(profiler) => Some(profiler), + }; + + let params = Oauth2Params { + name, + scopes: &self.scopes, + profiler, + authorize_url, + access_url, + profile_url, + }; + + params.try_into() + } +} + +pub struct DigitalOcean; + +impl StdOauth2Provider for DigitalOcean { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "digitalocean", + authorize_url: "https://cloud.digitalocean.com/v1/oauth/authorize", + access_url: "https://cloud.digitalocean.com/v1/oauth/token", + // https://docs.digitalocean.com/reference/api/api-reference/#operation/account_get + profile_url: Some("https://api.digitalocean.com/v2/account"), + } + } + + fn default_profiler(&self) -> Result { + let mut account = t::struct_(); + let inp = t::struct_() + .propx("account", account.propx("uuid", t::string())?)? + .build()?; + let out = t::struct_().propx("id", t::integer())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.account.uuid})".to_string(), + }) + } +} + +pub struct Discord; + +impl StdOauth2Provider for Discord { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "discord", + authorize_url: "https://discord.com/api/oauth2/authorize", + access_url: "https://discord.com/api/oauth2/token", + // https://discord.com/developers/docs/resources/user + profile_url: Some("https://discord.com/api/users/@me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} +pub struct Dropbox; + +impl StdOauth2Provider for Dropbox { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "dropbox", + authorize_url: "https://www.dropbox.com/oauth2/authorize", + access_url: "https://api.dropboxapi.com/oauth2/token", + // https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account + profile_url: Some("https://api.dropboxapi.com/2/users/get_current_account"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("account_id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.account_id})".to_string(), + }) + } +} +pub struct Facebook; + +impl StdOauth2Provider for Facebook { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "facebook", + authorize_url: "https://www.facebook.com/v16.0/dialog/oauth", + access_url: "https://graph.facebook.com/v16.0/oauth/access_token", + // https://developers.facebook.com/docs/graph-api/overview#me + // https://developers.facebook.com/docs/graph-api/reference/user/ + profile_url: Some("https://graph.facebook.com/me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} +pub struct Github; + +impl StdOauth2Provider for Github { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "github", + authorize_url: "https://github.com/login/oauth/authorize", + access_url: "https://github.com/login/oauth/access_token", + // https://docs.github.com/en/rest/reference/users?apiVersion=2022-11-28#get-the-authenticated-user + profile_url: Some("https://api.github.com/user"), + } + } + + fn default_profiler(&self) -> Result { + // ?? are these input types correct? + let inp = t::struct_().propx("id", t::integer())?.build()?; + let out = t::struct_().propx("id", t::integer())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } + + fn extended_profiler(&self, extension: &str) -> Result { + let inp = t::struct_().propx("id", t::integer())?.build()?; + + let additional_fields: Vec = + serde_json::from_str(extension).map_err(|_| -> crate::errors::TgError { + format!( + "Failed to parse profiler extension: expected a JSON list of strings, got `{}`", + extension + ) + .into() + })?; + + let mut out = t::struct_(); + out.propx("id", t::integer())?; + + for field in additional_fields.iter() { + match field.as_str() { + "login" => out.propx("login", t::string())?, + "avatar_url" => out.propx("avatar_url", t::string())?, + "email" => out.propx("email", t::string().format("email").optional()?)?, + _ => { + return Err(format!( + "Unknown field `{}` in profiler extension: `{}`", + field, extension + ) + .into()) + } + }; + } + + let out = out.build()?; + + let js_code = format!( + "(p) => ({{ id: p.id, {}}})", + additional_fields + .iter() + .map(|f| format!("{}: p.{}, ", f, f)) + .collect::>() + .join("") + ); + + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code, + }) + } +} +pub struct Gitlab; + +impl StdOauth2Provider for Gitlab { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "gitlab", + authorize_url: "https://gitlab.com/oauth/authorize", + access_url: "https://gitlab.com/oauth/token", + // https://docs.gitlab.com/ee/api/users.html#list-current-user + profile_url: Some("https://gitlab.com/api/v3/user"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::integer())?.build()?; + let out = t::struct_().propx("id", t::integer())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} + +pub struct Google; + +impl StdOauth2Provider for Google { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "google", + authorize_url: "https://accounts.google.com/o/oauth2/v2/auth", + access_url: "https://oauth2.googleapis.com/token", + // https://cloud.google.com/identity-platform/docs/reference/rest/v1/UserInfo + profile_url: Some("https://openidconnect.googleapis.com/v1/userinfo"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("localId", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.localId})".to_string(), + }) + } +} + +pub struct Instagram; + +impl StdOauth2Provider for Instagram { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "instagram", + authorize_url: "https://api.instagram.com/oauth/authorize", + access_url: "https://api.instagram.com/oauth/access_token", + // https://developers.facebook.com/docs/instagram-basic-display-api/reference/me + // https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#reading + profile_url: Some("https://graph.instagram.com/me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} + +pub struct LinkedIn; + +impl StdOauth2Provider for LinkedIn { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "linkedin", + authorize_url: "https://www.linkedin.com/oauth/v2/authorization", + access_url: "https://www.linkedin.com/oauth/v2/accessToken", + // https://learn.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api#retrieve-current-members-profile + profile_url: Some("https://api.linkedin.com/v2/me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::integer())?.build()?; + let out = t::struct_().propx("id", t::integer())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} + +pub struct Microsoft; + +impl StdOauth2Provider for Microsoft { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "microsoft", + authorize_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + access_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token", + // https://learn.microsoft.com/en-us//javascript/api/@microsoft/teams-js/app.userinfo?view=msteams-client-js-latest + profile_url: Some("https://graph.microsoft.com/oidc/userinfo"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} + +pub struct Reddit; + +impl StdOauth2Provider for Reddit { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "reddit", + authorize_url: "https://www.reddit.com/api/v1/authorize", + access_url: "https://www.reddit.com/api/v1/access_token", + // https://www.reddit.com/dev/api/#GET_api_v1_me + profile_url: Some("https://oauth.reddit.com/api/v1/me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_() + .propx("id", t::eitherx!(t::integer(), t::string()))? + .build()?; + let out = t::struct_() + .propx("id", t::eitherx!(t::integer(), t::string()))? + .build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.id})".to_string(), + }) + } +} + +pub struct Slack; + +impl StdOauth2Provider for Slack { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "slack", + authorize_url: "https://slack.com/oauth/v2/authorize", + access_url: "https://slack.com/api/oauth.v2.access", + // https://api.slack.com/methods/auth.test + profile_url: Some("https://slack.com/api/auth.test"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("user_id", t::string())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.user_id})".to_string(), + }) + } +} + +pub struct StackExchange; + +impl StdOauth2Provider for StackExchange { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "stackexchange", + authorize_url: "https://stackoverflow.com/oauth", + access_url: "https://stackoverflow.com/oauth/access_token/json", + // https://api.stackexchange.com/docs/me + profile_url: Some("https://api.stackexchange.com/2.3/me"), + } + } + + fn default_profiler(&self) -> Result { + let inp = t::struct_().propx("account_id", t::integer())?.build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: `${p.account_id}`})".to_string(), + }) + } +} + +pub struct Twitter; + +impl StdOauth2Provider for Twitter { + fn get_static_params(&self) -> StaticParams { + StaticParams { + name: "twitter", + authorize_url: "https://twitter.com/i/oauth2/authorize", + access_url: "https://api.twitter.com/2/oauth2/token", + // https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me + profile_url: Some("https://api.twitter.com/2/users/me"), + } + } + + fn default_profiler(&self) -> Result { + let mut data = t::struct_(); + let inp = t::struct_() + .propx("data", data.propx("id", t::string())?)? + .build()?; + let out = t::struct_().propx("id", t::string())?.build()?; + Ok(OAuth2Profiler { + input: inp, + output: out, + js_code: "(p) => ({id: p.data.id})".to_string(), + }) + } +} + +pub fn named_provider(name: &str) -> Result> { + match name { + "digitalocean" => Ok(Box::new(DigitalOcean)), + "discord" => Ok(Box::new(Discord)), + "dropbox" => Ok(Box::new(Dropbox)), + "facebook" => Ok(Box::new(Facebook)), + "github" => Ok(Box::new(Github)), + "gitlab" => Ok(Box::new(Gitlab)), + "google" => Ok(Box::new(Google)), + "instagram" => Ok(Box::new(Instagram)), + "linkedin" => Ok(Box::new(LinkedIn)), + "microsoft" => Ok(Box::new(Microsoft)), + "reddit" => Ok(Box::new(Reddit)), + "slack" => Ok(Box::new(Slack)), + "stackexchange" => Ok(Box::new(StackExchange)), + "twitter" => Ok(Box::new(Twitter)), + _ => Err(format!("Unknown provider: {}", name).into()), + } +} diff --git a/typegraph/core/wit/typegraph.wit b/typegraph/core/wit/typegraph.wit index f3c0c9d1e5..ba4211f813 100644 --- a/typegraph/core/wit/typegraph.wit +++ b/typegraph/core/wit/typegraph.wit @@ -488,6 +488,9 @@ interface utils { add-auth: func(data: auth) -> result add-raw-auth: func(data: string) -> result oauth2: func(service-name: string, scopes: string) -> result + oauth2-without-profiler: func(service-name: string, scopes: string) -> result + oauth2-with-extended-profiler: func(service-name: string, scopes: string, extension: string) -> result + oauth2-with-custom-profiler: func(service-name: string, scopes: string, profiler: type-id) -> result } world typegraph { diff --git a/typegraph/deno/src/params.ts b/typegraph/deno/src/params.ts index 74e1dcb000..0c9bc045bb 100644 --- a/typegraph/deno/src/params.ts +++ b/typegraph/deno/src/params.ts @@ -3,6 +3,29 @@ import { RawAuth } from "./typegraph.ts"; import { Auth as Auth_, wit_utils } from "./wit.ts"; +import * as t from "./types.ts"; + +export type StdOauth2Profiler = + | { profiler: "default" } + | { profiler: "none" } + | { profiler: "extended", extension: any } + | { profiler: "custom", id: number }; + +export function noProfiler(): StdOauth2Profiler { + return { profiler: "none" }; +} + +export function defaultProfiler(): StdOauth2Profiler { + return { profiler: "default" }; +} + +export function extendedProfiler(extension: any): StdOauth2Profiler { + return { profiler: "extended", extension }; +} + +export function customProfiler(func: t.Typedef): StdOauth2Profiler { + return { profiler: "custom", id: func._id }; +} export class Auth { static jwt(name: string, format: string, algorithm?: any): Auth_ { @@ -39,59 +62,72 @@ export class Auth { }; } - static oauth2Digitalocean(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("digitalocean", scopes)); + private static stdOauth2(provider: string, scopes: string, profiler: StdOauth2Profiler): RawAuth { + switch (profiler.profiler) { + case "none": + return new RawAuth(wit_utils.oauth2WithoutProfiler(provider, scopes)); + case "extended": + return new RawAuth(wit_utils.oauth2WithExtendedProfiler(provider, scopes, JSON.stringify(profiler.extension))); + case "custom": + return new RawAuth(wit_utils.oauth2WithCustomProfiler(provider, scopes, profiler.id)); + default: + return new RawAuth(wit_utils.oauth2(provider, scopes)); + } + } + + static oauth2Digitalocean(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("digitalocean", scopes, profiler ?? defaultProfiler()); } - static oauth2Discord(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("discord", scopes)); + static oauth2Discord(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("discord", scopes, profiler ?? defaultProfiler()); } - static oauth2Dropbox(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("dropbox", scopes)); + static oauth2Dropbox(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("dropbox", scopes, profiler ?? defaultProfiler()); } - static oauth2Facebook(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("facebook", scopes)); + static oauth2Facebook(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("facebook", scopes, profiler ?? defaultProfiler()); } - static oauth2Github(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("github", scopes)); + static oauth2Github(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("github", scopes, profiler ?? defaultProfiler()); } - static oauth2Gitlab(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("gitlab", scopes)); + static oauth2Gitlab(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("gitlab", scopes, profiler ?? defaultProfiler()); } - static oauth2Google(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("google", scopes)); + static oauth2Google(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("google", scopes, profiler ?? defaultProfiler()); } - static oauth2Instagram(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("instagram", scopes)); + static oauth2Instagram(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("instagram", scopes, profiler ?? defaultProfiler()); } - static oauth2Linkedin(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("linkedin", scopes)); + static oauth2Linkedin(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("linkedin", scopes, profiler ?? defaultProfiler()); } - static oauth2Microsoft(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("microsoft", scopes)); + static oauth2Microsoft(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("microsoft", scopes, profiler ?? defaultProfiler()); } - static oauth2Reddit(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("reddit", scopes)); + static oauth2Reddit(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("reddit", scopes, profiler ?? defaultProfiler()); } - static oauth2Slack(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("slack", scopes)); + static oauth2Slack(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("slack", scopes, profiler ?? defaultProfiler()); } - static oauth2Stackexchange(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("stackexchange", scopes)); + static oauth2Stackexchange(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("stackexchange", scopes, profiler ?? defaultProfiler()); } - static oauth2Twitter(scopes: string): RawAuth { - return new RawAuth(wit_utils.oauth2("twitter", scopes)); + static oauth2Twitter(scopes: string, profiler?: StdOauth2Profiler): RawAuth { + return Auth.stdOauth2("twitter", scopes, profiler ?? defaultProfiler()); } } diff --git a/typegraph/python/typegraph/graph/params.py b/typegraph/python/typegraph/graph/params.py index 44e4cf8573..d59cecff3e 100644 --- a/typegraph/python/typegraph/graph/params.py +++ b/typegraph/python/typegraph/graph/params.py @@ -3,7 +3,7 @@ from dataclasses import dataclass import json -from typing import List, Optional, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING, Any from typegraph.gen.exports import utils from typegraph.wit import store, wit_utils @@ -13,6 +13,24 @@ from typegraph import t +class StdOauth2Profiler: + pass + + +class NoProfiler(StdOauth2Profiler): + pass + + +@dataclass +class ExtendedProfiler(StdOauth2Profiler): + extension: Any + + +@dataclass +class CustomProfiler(StdOauth2Profiler): + id: "t.func" + + class Rate: window_limit: int window_sec: int @@ -106,95 +124,102 @@ def oauth2( ], ) - def oauth2_digitalocean(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "digitalocean", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_digitalocean( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("digitalocean", scopes, profiler) - def oauth2_discord(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "discord", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_discord( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("discord", scopes, profiler) - def oauth2_dropbox(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "dropbox", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_dropbox( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("dropbox", scopes, profiler) - def oauth2_facebook(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "facebook", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_facebook( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("facebook", scopes, profiler) - def oauth2_github(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "github", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_github( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("github", scopes, profiler) - def oauth2_gitlab(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "gitlab", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_gitlab( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("gitlab", scopes, profiler) - def oauth2_google(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "google", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_google( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("google", scopes, profiler) - def oauth2_instagram(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "instagram", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_instagram( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("instagram", scopes, profiler) - def oauth2_linkedin(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "linkedin", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_linkedin( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("linkedin", scopes, profiler) - def oauth2_microsoft(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "microsoft", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_microsoft( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("microsoft", scopes, profiler) - def oauth2_reddit(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "reddit", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_reddit( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("reddit", scopes, profiler) - def oauth2_slack(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "slack", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_slack( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("slack", scopes, profiler) - def oauth2_stackexchange(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "stackexchange", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_stackexchange( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("stackexchange", scopes, profiler) - def oauth2_twitter(scopes: str) -> "utils.Auth": - res = wit_utils.oauth2(store, "twitter", scopes) - if isinstance(res, Err): - raise Exception(res.value) - return RawAuth(res.value) + def oauth2_twitter( + scopes: str, profiler: Optional[StdOauth2Profiler] = None + ) -> "utils.Auth": + return RawAuth.from_std("twitter", scopes, profiler) @dataclass class RawAuth: json_str: str + @classmethod + def from_std( + cls, service: str, scopes: str, profiler: Optional[StdOauth2Profiler] = None + ): + if isinstance(profiler, NoProfiler): + res = wit_utils.oauth2_without_profiler(store, service, scopes) + elif isinstance(profiler, ExtendedProfiler): + res = wit_utils.oauth2_with_extended_profiler( + store, service, scopes, json.dumps(profiler.extension) + ) + elif isinstance(profiler, CustomProfiler): + res = wit_utils.oauth2_with_custom_profiler( + store, service, scopes, profiler.id + ) + else: # default profiler + res = wit_utils.oauth2(store, service, scopes) + + if isinstance(res, Err): + raise Exception(res.value) + return cls(res.value) + @dataclass class OAuth2Params: