From ffdd2c6ca9132150707e63930df92ee5f11a31a0 Mon Sep 17 00:00:00 2001 From: Nick Hale <4175918+njhale@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:34:48 -0500 Subject: [PATCH] feat: add atlassian oauth app type and defaults Signed-off-by: Nick Hale <4175918+njhale@users.noreply.github.com> --- apiclient/types/oauthapp.go | 1 + pkg/gateway/server/oauth_apps.go | 8 ++ pkg/gateway/types/oauth_apps.go | 6 ++ .../components/oauth-apps/DeleteOAuthApp.tsx | 20 +++- .../components/oauth-apps/OAuthAppDetail.tsx | 17 ++- .../components/oauth-apps/OAuthAppTile.tsx | 15 ++- .../oauth-apps/OAuthAppTypeIcon.tsx | 9 +- ui/admin/app/lib/model/oauthApps/index.ts | 2 + .../app/lib/model/oauthApps/oauth-helpers.ts | 2 + .../model/oauthApps/providers/atlassian.ts | 102 ++++++++++++++++++ .../lib/model/oauthApps/providers/google.ts | 2 +- ui/admin/public/assets/atlassian_logo.svg | 35 ++++++ 12 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 ui/admin/app/lib/model/oauthApps/providers/atlassian.ts create mode 100644 ui/admin/public/assets/atlassian_logo.svg diff --git a/apiclient/types/oauthapp.go b/apiclient/types/oauthapp.go index 1e52404a..190757e5 100644 --- a/apiclient/types/oauthapp.go +++ b/apiclient/types/oauthapp.go @@ -1,6 +1,7 @@ package types const ( + OAuthAppTypeAtlassian OAuthAppType = "atlassian" OAuthAppTypeMicrosoft365 OAuthAppType = "microsoft365" OAuthAppTypeSlack OAuthAppType = "slack" OAuthAppTypeNotion OAuthAppType = "notion" diff --git a/pkg/gateway/server/oauth_apps.go b/pkg/gateway/server/oauth_apps.go index 41cfed1a..fe4f5f7f 100644 --- a/pkg/gateway/server/oauth_apps.go +++ b/pkg/gateway/server/oauth_apps.go @@ -246,6 +246,14 @@ func (s *Server) authorizeOAuthApp(apiContext api.Context) error { q.Set("optional_scope", app.Spec.Manifest.OptionalScope) } + // Atlassian requires the audience and prompt parameters to be set. + // See https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/#1--direct-the-user-to-the-authorization-url-to-get-an-authorization-code + // for details. + if app.Spec.Manifest.Type == types2.OAuthAppTypeAtlassian { + q.Set("audience", "api.atlassian.com") + q.Set("prompt", "consent") + } + // For Google: access_type=offline instructs Google to return a refresh token and an access token on the initial authorization. // This can be used to refresh the access token when a user is not present at the browser // prompt=consent instructs Google to show the consent screen every time the authorization flow happens so that we get a new refresh token. diff --git a/pkg/gateway/types/oauth_apps.go b/pkg/gateway/types/oauth_apps.go index b7bd2c60..7eaf6a53 100644 --- a/pkg/gateway/types/oauth_apps.go +++ b/pkg/gateway/types/oauth_apps.go @@ -11,6 +11,9 @@ import ( ) const ( + AtlassianAuthorizeURL = "https://auth.atlassian.com/authorize" + AtlassianTokenURL = "https://auth.atlassian.com/oauth/token" + SlackAuthorizeURL = "https://slack.com/oauth/v2/authorize" SlackTokenURL = "https://slack.com/api/oauth.v2.access" @@ -44,6 +47,9 @@ func ValidateAndSetDefaultsOAuthAppManifest(r *types.OAuthAppManifest, create bo } switch r.Type { + case types.OAuthAppTypeAtlassian: + r.AuthURL = AtlassianAuthorizeURL + r.TokenURL = AtlassianTokenURL case types.OAuthAppTypeMicrosoft365: r.AuthURL = fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", r.TenantID) r.TokenURL = fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", r.TenantID) diff --git a/ui/admin/app/components/oauth-apps/DeleteOAuthApp.tsx b/ui/admin/app/components/oauth-apps/DeleteOAuthApp.tsx index 0547703c..86a3c0fe 100644 --- a/ui/admin/app/components/oauth-apps/DeleteOAuthApp.tsx +++ b/ui/admin/app/components/oauth-apps/DeleteOAuthApp.tsx @@ -33,16 +33,28 @@ export function DeleteOAuthApp({ toast.success(`${spec.displayName} OAuth configuration deleted`); }); + const title = spec.noGatewayIntegration + ? `Delete ${spec.displayName} OAuth` + : `Reset ${spec.displayName} OAuth to use Acorn Gateway`; + + const description = spec.noGatewayIntegration + ? `By clicking \`Delete\`, you will delete your ${spec.displayName} OAuth configuration.` + : `By clicking \`Reset\`, you will delete your custom ${spec.displayName} OAuth configuration and reset to use Acorn Gateway.`; + + const buttonText = spec.noGatewayIntegration + ? `Delete ${spec.displayName} OAuth` + : `Reset ${spec.displayName} OAuth to use Acorn Gateway`; + return (
@@ -54,7 +66,7 @@ export function DeleteOAuthApp({ {deleteOAuthApp.isLoading ? ( ) : null} - Reset {spec.displayName} OAuth to use Acorn Gateway + {buttonText} diff --git a/ui/admin/app/components/oauth-apps/OAuthAppDetail.tsx b/ui/admin/app/components/oauth-apps/OAuthAppDetail.tsx index f3f9e4bf..c29ba189 100644 --- a/ui/admin/app/components/oauth-apps/OAuthAppDetail.tsx +++ b/ui/admin/app/components/oauth-apps/OAuthAppDetail.tsx @@ -124,7 +124,22 @@ function EmptyContent({ spec: OAuthAppSpec; onSuccess: () => void; }) { - return ( + return spec.noGatewayIntegration ? ( +
+ + {spec.displayName} OAuth is not configured. You must configure + it to enable tools that interact with protected{" "} + {spec.displayName} APIs. + + + + You can also configure {spec.displayName} OAuth by clicking the + button below. + + + +
+ ) : (
{spec.displayName} OAuth is currently enabled. No action is diff --git a/ui/admin/app/components/oauth-apps/OAuthAppTile.tsx b/ui/admin/app/components/oauth-apps/OAuthAppTile.tsx index c4f50be4..ef96cc2f 100644 --- a/ui/admin/app/components/oauth-apps/OAuthAppTile.tsx +++ b/ui/admin/app/components/oauth-apps/OAuthAppTile.tsx @@ -57,10 +57,22 @@ export function OAuthAppTile({ organization. + ) : info.noGatewayIntegration ? ( + + + + Not Configured + + + + + OAuth for {displayName} is not configured + + ) : ( - Default + Default Configured @@ -73,7 +85,6 @@ export function OAuthAppTile({ -
[] = [ + { + type: "markdown", + text: + "### Step 1: Create a new Atlassian OAuth 2.0 Integration\n" + + "- Navigate to [Create a new OAuth 2.0 (3LO) integration](https://developer.atlassian.com/console/myapps/create-3lo-app)\n" + + "- Enter `Acorn` as the integration name.\n" + + "- Click the checkbox to the terms and conditions.\n" + + "- Click the `Create` button.\n", + }, + { + type: "markdown", + text: + "### Step 2: Configure OAuth Scopes\n" + + "Configure required OAuth Scopes by completing both sections below.\n", + }, + { + type: "sectionGroup", + sections: [ + { + title: "User identity API Scopes", + steps: [ + { + type: "markdown", + text: + "- Navigate to the `Permissions` tab in the sidebar.\n" + + "- Click on the `Add` button for `User identity API`\n" + + "- Click on the `Configure` button for `User identity API`\n" + + "- Click on the `Edit Scopes` button to open the `Edit User identity API` modal.\n" + + "- Click the checkboxes to select the `read:me` and `read:account` scopes.\n" + + "- Click on the `Save` button.\n", + }, + ], + }, + { + title: "Jira API Scopes", + steps: [ + { + type: "markdown", + text: + "- Navigate to the `Permissions` tab in the sidebar.\n" + + "- Click on the `Add` button for `Jira API`\n" + + "- Click on the `Configure` button for `Jira API`\n" + + "- Click on the `Edit Scopes` button to open the `Edit Jira API` modal.\n" + + "- Click the checkboxes to select the `read:jira-work`, `write:jira-work`, and `read:jira-user` scopes.\n" + + "- Click on the `Save` button.\n", + }, + ], + }, + ], + }, + { + type: "markdown", + text: + "### Step 3: Configure your OAuth Consent Screen\n" + + "- Navigate to the `Authorization` tab in the sidebar.\n" + + "- Click on the `Add` button for `OAuth 2.0 (3LO)`.\n" + + "- Enter the URL below in the `Callback URL` box and click on the `Save changes` button:\n", + }, + { + type: "copy", + text: getOAuthLinks("atlassian").redirectURL, + }, + { + type: "markdown", + text: + "### Step 4: Register your OAuth App credentials with Acorn\n" + + "- Navigate to the `Settings` tab in the sidebar.\n" + + "- Enter the `Client ID` and `Client Secret` from the `Authentication details` section into the fields below\n", + }, + { type: "input", input: "clientID", label: "Client ID" }, + { + type: "input", + input: "clientSecret", + label: "Client Secret", + inputType: "password", + }, +]; + +export const AtlassianOAuthApp = { + schema, + alias: "atlassian", + type: "atlassian", + displayName: "Atlassian", + logo: assetUrl("/assets/atlassian_logo.svg"), + steps: steps, + noGatewayIntegration: true, +} satisfies OAuthAppSpec; diff --git a/ui/admin/app/lib/model/oauthApps/providers/google.ts b/ui/admin/app/lib/model/oauthApps/providers/google.ts index e6b250f8..766d61e2 100644 --- a/ui/admin/app/lib/model/oauthApps/providers/google.ts +++ b/ui/admin/app/lib/model/oauthApps/providers/google.ts @@ -31,7 +31,7 @@ const steps: OAuthFormStep[] = [ type: "markdown", text: "### Step 1: Create a new Google Project\n" + - "- Navigate to the [Credentials](https://console.cloud.google.com/apis/credentials) section of the APIs & Serivces page in your [Google API Dashboard](https://console.cloud.google.com).\n" + + "- Navigate to the [Credentials](https://console.cloud.google.com/apis/credentials) section of the APIs & Services page in your [Google API Dashboard](https://console.cloud.google.com).\n" + "- If you already have a Google Project Setup, skip to Step 2.", }, { diff --git a/ui/admin/public/assets/atlassian_logo.svg b/ui/admin/public/assets/atlassian_logo.svg new file mode 100644 index 00000000..0fa351ed --- /dev/null +++ b/ui/admin/public/assets/atlassian_logo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +