Repository navigation
feat(standardized-auth): add an acme sample for standardized app auth #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
WilliamBergamin
wants to merge
10
commits into
main
Choose a base branch
from
standardized-auth
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
61f9bd1
feat(standardized-auth): add an acme sample for standardized app auth
WilliamBergamin 6be43df
refactor(standardized-auth): mirror the python sample over socket mode
WilliamBergamin e9e39a2
test(standardized-auth): pin the user_connection routing key
WilliamBergamin a0deaea
feat(standardized-auth): log slack api calls at debug like the python…
WilliamBergamin 0cca724
docs(standardized-auth): name the java and maven versions the sample …
WilliamBergamin 59e95f5
refactor(standardized-auth): use the sdk's user_connection event model
WilliamBergamin b17dfda
build(standardized-auth): log with logback like the starter template
WilliamBergamin 24e8d07
Merge branch 'main' into standardized-auth
WilliamBergamin 1fbb032
build(standardized-auth): pin bolt 1.53.0 ahead of its release
WilliamBergamin c8c363d
Merge branch 'main' into standardized-auth
WilliamBergamin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| .classpath | ||
| .project | ||
| .settings | ||
| target | ||
| .env* | ||
| acme.properties |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| apps.dev.json | ||
| cache/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "manifest": { | ||
| "source": "local" | ||
| }, | ||
| "project_id": "00000000-0000-0000-0000-000000000000" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "hooks": { | ||
| "start": "mvn --quiet compile exec:java" | ||
| }, | ||
| "config": { | ||
| "sdk-managed-connection-enabled": true | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Standardized Auth | ||
|
|
||
| Connect your Acme account to Slack with the Slack-owned **Connect** and **Manage connection** controls. Rocket skates, anvils, and giant magnets not included. | ||
|
|
||
| Read the docs: https://docs.slack.dev/authentication/standardized-auth | ||
|
|
||
| ## How it works | ||
|
|
||
| Acme runs over Socket Mode and keeps every connection in a local properties file ([`Connections.java`](./src/main/java/acme/Connections.java)): a user is connected when their Slack user ID is in it. The provider object and the App Home view live in [`Acme.java`](./src/main/java/acme/Acme.java), and every listener and Slack API call lives in [`Main.java`](./src/main/java/acme/Main.java). | ||
|
|
||
| - **Connect** connects the Slack user who clicked it, with their user ID as the connected identity. It reports the connection with `apps.user.connection.requestAuth` and refreshes App Home. | ||
| - **Connect** or **Manage** on the app's profile sends the `user_connection` event, and Acme answers within 3 seconds by opening the connection modal with `apps.user.connection.requestAuth` and the event's `trigger_id`. | ||
| - **Disconnect** forgets the connection and reports the user as disconnected. | ||
| - **`user_connection_status_requested`** answers with `apps.user.connection.update` whenever Slack asks about a user it doesn't know yet. | ||
| - **App Home** shows an `auth_provider` block with the user's current connection. | ||
|
|
||
| Connecting in one click stands in for your service's real login. Slack requires at least one host in the manifest's `features.user_connections.auth_hosts`, so `example.com` holds the place until you have one. To sign users in to your own service, set `auth_url` on the provider to your OAuth or SSO page, replace `example.com` with its host, and carry the Slack user through the browser in a signed `state` so a connection always lands on the user who clicked **Connect**. | ||
|
|
||
| ## Setup | ||
|
|
||
| Everything arrives over Socket Mode, so the app needs no public URL. The manifest subscribes to the `user_connection` and `user_connection_status_requested` events, which every app with `features.user_connections` needs. Use a Slack CLI newer than v4.10.0: older versions don't send `features.user_connections` with the manifest. | ||
|
|
||
| Acme runs on Java 17 with Maven. `slack run` starts it with `mvn compile exec:java`, the `start` hook in [`.slack/hooks.json`](./.slack/hooks.json), and every Slack API call and its response is logged at debug ([`logback.xml`](./src/main/resources/logback.xml)). | ||
|
|
||
| ```sh | ||
| $ slack run | ||
| ``` | ||
|
|
||
| Then open the app's **Home** tab and click **Connect**. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| { | ||
| "display_information": { | ||
| "name": "Acme", | ||
| "description": "Rocket skates, anvils, and giant magnets for every plan" | ||
| }, | ||
| "features": { | ||
| "app_home": { | ||
| "home_tab_enabled": true | ||
| }, | ||
| "bot_user": { | ||
| "display_name": "Acme", | ||
| "always_online": true | ||
| }, | ||
| "user_connections": { | ||
| "is_enabled": true, | ||
| "auth_hosts": ["example.com"] | ||
| } | ||
| }, | ||
| "oauth_config": { | ||
| "scopes": { | ||
| "bot": ["users:read"] | ||
| } | ||
| }, | ||
| "settings": { | ||
| "event_subscriptions": { | ||
| "bot_events": [ | ||
| "app_home_opened", | ||
| "user_connection", | ||
| "user_connection_status_requested" | ||
| ] | ||
| }, | ||
| "interactivity": { | ||
| "is_enabled": true | ||
| }, | ||
| "org_deploy_enabled": true, | ||
| "socket_mode_enabled": true, | ||
| "token_rotation_enabled": false | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <groupId>com.slack.api</groupId> | ||
| <artifactId>standardized-auth-example</artifactId> | ||
| <version>0.1-SNAPSHOT</version> | ||
| <packaging>jar</packaging> | ||
| <properties> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <exec.mainClass>acme.Main</exec.mainClass> | ||
| </properties> | ||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-compiler-plugin</artifactId> | ||
| <version>3.16.0</version> | ||
| <configuration> | ||
| <release>17</release> | ||
| </configuration> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>org.codehaus.mojo</groupId> | ||
| <artifactId>exec-maven-plugin</artifactId> | ||
| <version>3.6.4</version> | ||
| <configuration> | ||
| <cleanupDaemonThreads>false</cleanupDaemonThreads> | ||
| </configuration> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>com.diffplug.spotless</groupId> | ||
| <artifactId>spotless-maven-plugin</artifactId> | ||
| <version>3.10.4</version> | ||
| <configuration> | ||
| <formats> | ||
| <format> | ||
| <includes> | ||
| <include>.gitignore</include> | ||
| </includes> | ||
| <trimTrailingWhitespace/> | ||
| <endWithNewline/> | ||
| <indent> | ||
| <tabs>true</tabs> | ||
| <spacesPerTab>4</spacesPerTab> | ||
| </indent> | ||
| </format> | ||
| </formats> | ||
| <java> | ||
| <palantirJavaFormat/> | ||
| </java> | ||
| <pom/> | ||
| <markdown> | ||
| <includes> | ||
| <include>**/*.md</include> | ||
| </includes> | ||
| <flexmark/> | ||
| </markdown> | ||
| </configuration> | ||
| <executions> | ||
| <execution> | ||
| <goals> | ||
| <goal>apply</goal> | ||
| </goals> | ||
| <phase>compile</phase> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>com.slack.api</groupId> | ||
| <artifactId>bolt</artifactId> | ||
| <version>1.53.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.slack.api</groupId> | ||
| <artifactId>bolt-socket-mode</artifactId> | ||
| <version>1.53.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>javax.websocket</groupId> | ||
| <artifactId>javax.websocket-api</artifactId> | ||
| <version>1.1</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.glassfish.tyrus.bundles</groupId> | ||
| <artifactId>tyrus-standalone-client</artifactId> | ||
| <version>1.21</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>ch.qos.logback</groupId> | ||
| <artifactId>logback-classic</artifactId> | ||
| <version>1.6.5</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter</artifactId> | ||
| <version>6.1.3</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.mockito</groupId> | ||
| <artifactId>mockito-junit-jupiter</artifactId> | ||
| <version>5.23.0</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package acme; | ||
|
|
||
| import static com.slack.api.model.block.Blocks.asBlocks; | ||
| import static com.slack.api.model.block.Blocks.authProvider; | ||
| import static com.slack.api.model.block.Blocks.section; | ||
| import static com.slack.api.model.block.composition.BlockCompositions.markdownText; | ||
| import static com.slack.api.model.block.composition.BlockCompositions.plainText; | ||
| import static com.slack.api.model.view.Views.view; | ||
|
|
||
| import com.slack.api.methods.request.apps.user.connection.AppsUserConnectionRequestAuthRequest.PrimaryProvider; | ||
| import com.slack.api.model.view.View; | ||
|
|
||
| public final class Acme { | ||
| public static final String PROVIDER_VALUE = "acme"; | ||
|
|
||
| private Acme() {} | ||
|
|
||
| public static PrimaryProvider provider(String userId) { | ||
| PrimaryProvider acme = PrimaryProvider.builder() | ||
| .value(PROVIDER_VALUE) | ||
| .name(plainText("Acme account")) | ||
| .description(plainText("Order rocket skates, anvils, and giant magnets without leaving Slack.")) | ||
| .isConnected(Connections.isConnected(userId)) | ||
| .build(); | ||
| if (acme.getIsConnected()) { | ||
| acme.setConnectedIdentity(plainText(userId)); | ||
| } | ||
| return acme; | ||
| } | ||
|
|
||
| public static View homeView(PrimaryProvider acme) { | ||
| return view(view -> view.type("home") | ||
| .blocks(asBlocks( | ||
| section(section -> section.text(markdownText("*Welcome to Acme!* :rocket:\n" | ||
| + "Anvils ship same day, gravity permitting. " | ||
| + "Please keep giant magnets away from your laptop."))), | ||
| authProvider(block -> block.value(acme.getValue()) | ||
| .name(acme.getName()) | ||
| .description(acme.getDescription()) | ||
| .iconUrl(acme.getIconUrl()) | ||
| .authUrl(acme.getAuthUrl()) | ||
| .disconnectUrl(acme.getDisconnectUrl()) | ||
| .isConnected(acme.getIsConnected()) | ||
| .connectedIdentity(acme.getConnectedIdentity()) | ||
| .connectedContext(acme.getConnectedContext()) | ||
| .accessory(acme.getAccessory()))))); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package acme; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.Reader; | ||
| import java.io.UncheckedIOException; | ||
| import java.io.Writer; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.Properties; | ||
|
|
||
| public final class Connections { | ||
| static Path filename = Path.of("acme.properties"); | ||
|
|
||
| private Connections() {} | ||
|
|
||
| public static void connect(String userId) { | ||
| Properties db = read(); | ||
| db.setProperty(userId, "true"); | ||
| write(db); | ||
| } | ||
|
|
||
| public static void disconnect(String userId) { | ||
| Properties db = read(); | ||
| db.remove(userId); | ||
| write(db); | ||
| } | ||
|
|
||
| public static boolean isConnected(String userId) { | ||
| return read().containsKey(userId); | ||
| } | ||
|
|
||
| private static Properties read() { | ||
| Properties db = new Properties(); | ||
| if (Files.notExists(filename)) { | ||
| return db; | ||
| } | ||
| try (Reader reader = Files.newBufferedReader(filename)) { | ||
| db.load(reader); | ||
| } catch (IOException error) { | ||
| throw new UncheckedIOException(error); | ||
| } | ||
| return db; | ||
| } | ||
|
|
||
| private static void write(Properties db) { | ||
| try (Writer writer = Files.newBufferedWriter(filename)) { | ||
| db.store(writer, null); | ||
| } catch (IOException error) { | ||
| throw new UncheckedIOException(error); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📚 issue (blocking): Let's move this to
authentication/standardized-auth/please! The examples mirror the docs.slack.dev paths withoutreference, likeblock-kit/andmethods/, and the docs page for this lives at/authentication/standardized-auth. The rootREADME.md,.github/dependabot.ymland workflow paths will need to follow.