diff --git a/packages/backend/src/apps/bluesky/assets/favicon.svg b/packages/backend/src/apps/bluesky/assets/favicon.svg
new file mode 100644
index 00000000..42795eb2
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/assets/favicon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/backend/src/apps/bluesky/auth/index.js b/packages/backend/src/apps/bluesky/auth/index.js
new file mode 100644
index 00000000..4e9080af
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/auth/index.js
@@ -0,0 +1,34 @@
+import verifyCredentials from './verify-credentials.js';
+import isStillVerified from './is-still-verified.js';
+import refreshToken from './refresh-token.js';
+
+export default {
+ fields: [
+ {
+ key: 'handle',
+ label: 'Your Bluesky Handle',
+ type: 'string',
+ required: true,
+ readOnly: false,
+ value: null,
+ placeholder: null,
+ description: '',
+ clickToCopy: false,
+ },
+ {
+ key: 'password',
+ label: 'Your Bluesky Password',
+ type: 'string',
+ required: true,
+ readOnly: false,
+ value: null,
+ placeholder: null,
+ description: '',
+ clickToCopy: false,
+ },
+ ],
+
+ verifyCredentials,
+ isStillVerified,
+ refreshToken,
+};
diff --git a/packages/backend/src/apps/bluesky/auth/is-still-verified.js b/packages/backend/src/apps/bluesky/auth/is-still-verified.js
new file mode 100644
index 00000000..869a8066
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/auth/is-still-verified.js
@@ -0,0 +1,8 @@
+import getCurrentUser from '../common/get-current-user.js';
+
+const isStillVerified = async ($) => {
+ const currentUser = await getCurrentUser($);
+ return !!currentUser.did;
+};
+
+export default isStillVerified;
diff --git a/packages/backend/src/apps/bluesky/auth/refresh-token.js b/packages/backend/src/apps/bluesky/auth/refresh-token.js
new file mode 100644
index 00000000..6c2981a2
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/auth/refresh-token.js
@@ -0,0 +1,24 @@
+const refreshToken = async ($) => {
+ const { refreshJwt } = $.auth.data;
+
+ const { data } = await $.http.post(
+ '/com.atproto.server.refreshSession',
+ null,
+ {
+ headers: {
+ Authorization: `Bearer ${refreshJwt}`,
+ },
+ additionalProperties: {
+ skipAddingAuthHeader: true,
+ },
+ }
+ );
+
+ await $.auth.set({
+ accessJwt: data.accessJwt,
+ refreshJwt: data.refreshJwt,
+ did: data.did,
+ });
+};
+
+export default refreshToken;
diff --git a/packages/backend/src/apps/bluesky/auth/verify-credentials.js b/packages/backend/src/apps/bluesky/auth/verify-credentials.js
new file mode 100644
index 00000000..1b865de9
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/auth/verify-credentials.js
@@ -0,0 +1,20 @@
+const verifyCredentials = async ($) => {
+ const handle = $.auth.data.handle;
+ const password = $.auth.data.password;
+
+ const body = {
+ identifier: handle,
+ password,
+ };
+
+ const { data } = await $.http.post('/com.atproto.server.createSession', body);
+
+ await $.auth.set({
+ accessJwt: data.accessJwt,
+ refreshJwt: data.refreshJwt,
+ did: data.did,
+ screenName: data.handle,
+ });
+};
+
+export default verifyCredentials;
diff --git a/packages/backend/src/apps/bluesky/common/add-auth-header.js b/packages/backend/src/apps/bluesky/common/add-auth-header.js
new file mode 100644
index 00000000..9c977c8b
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/common/add-auth-header.js
@@ -0,0 +1,12 @@
+const addAuthHeader = ($, requestConfig) => {
+ if (requestConfig.additionalProperties?.skipAddingAuthHeader)
+ return requestConfig;
+
+ if ($.auth.data?.accessJwt) {
+ requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessJwt}`;
+ }
+
+ return requestConfig;
+};
+
+export default addAuthHeader;
diff --git a/packages/backend/src/apps/bluesky/common/get-current-user.js b/packages/backend/src/apps/bluesky/common/get-current-user.js
new file mode 100644
index 00000000..f6dd3192
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/common/get-current-user.js
@@ -0,0 +1,15 @@
+const getCurrentUser = async ($) => {
+ const handle = $.auth.data.handle;
+
+ const params = {
+ actor: handle,
+ };
+
+ const { data: currentUser } = await $.http.get('/app.bsky.actor.getProfile', {
+ params,
+ });
+
+ return currentUser;
+};
+
+export default getCurrentUser;
diff --git a/packages/backend/src/apps/bluesky/index.js b/packages/backend/src/apps/bluesky/index.js
new file mode 100644
index 00000000..6f856a50
--- /dev/null
+++ b/packages/backend/src/apps/bluesky/index.js
@@ -0,0 +1,16 @@
+import defineApp from '../../helpers/define-app.js';
+import addAuthHeader from './common/add-auth-header.js';
+import auth from './auth/index.js';
+
+export default defineApp({
+ name: 'Bluesky',
+ key: 'bluesky',
+ iconUrl: '{BASE_URL}/apps/bluesky/assets/favicon.svg',
+ authDocUrl: '{DOCS_URL}/apps/bluesky/connection',
+ supportsConnections: true,
+ baseUrl: 'https://bluesky.app',
+ apiBaseUrl: 'https://bsky.social/xrpc',
+ primaryColor: '1185fd',
+ beforeRequest: [addAuthHeader],
+ auth,
+});
diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js
index 219a1d51..a9cc8a23 100644
--- a/packages/docs/pages/.vitepress/config.js
+++ b/packages/docs/pages/.vitepress/config.js
@@ -50,6 +50,12 @@ export default defineConfig({
{ text: 'Connection', link: '/apps/appwrite/connection' },
],
},
+ {
+ text: 'Bluesky',
+ collapsible: true,
+ collapsed: true,
+ items: [{ text: 'Connection', link: '/apps/bluesky/connection' }],
+ },
{
text: 'Carbone',
collapsible: true,
diff --git a/packages/docs/pages/apps/bluesky/connection.md b/packages/docs/pages/apps/bluesky/connection.md
new file mode 100644
index 00000000..3953e03c
--- /dev/null
+++ b/packages/docs/pages/apps/bluesky/connection.md
@@ -0,0 +1,10 @@
+# Bluesky
+
+:::info
+This page explains the steps you need to follow to set up the Bluesky connection in Automatisch. If any of the steps are outdated, please let us know!
+:::
+
+1. Enter your `Bluesky Handle` from the page to the `Your Bluesky Handle` field on Automatisch.
+1. Enter your `Bluesky Password` from the page to the `Your Bluesky Password` field on Automatisch.
+1. Click **Submit** button on Automatisch.
+1. Congrats! Start using your new Bluesky connection within the flows.
diff --git a/packages/docs/pages/public/favicons/bluesky.svg b/packages/docs/pages/public/favicons/bluesky.svg
new file mode 100644
index 00000000..42795eb2
--- /dev/null
+++ b/packages/docs/pages/public/favicons/bluesky.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file