Skip to content
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

Add Eartho Sign In Integration to StackAuth #342

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ WHERE "type" = 'FACEBOOK';

-- AlterEnum
BEGIN;
CREATE TYPE "ProxiedOAuthProviderType_new" AS ENUM ('GITHUB', 'GOOGLE', 'MICROSOFT', 'SPOTIFY');
CREATE TYPE "ProxiedOAuthProviderType_new" AS ENUM ('GITHUB', 'GOOGLE', 'MICROSOFT', 'SPOTIFY', 'EARTHO');
ALTER TABLE "ProxiedOAuthProviderConfig" ALTER COLUMN "type" TYPE "ProxiedOAuthProviderType_new" USING ("type"::text::"ProxiedOAuthProviderType_new");
ALTER TYPE "ProxiedOAuthProviderType" RENAME TO "ProxiedOAuthProviderType_old";
ALTER TYPE "ProxiedOAuthProviderType_new" RENAME TO "ProxiedOAuthProviderType";
Expand Down
1 change: 1 addition & 0 deletions apps/backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ enum ProxiedOAuthProviderType {
GITHUB
GOOGLE
MICROSOFT
EARTHO
SPOTIFY
}

Expand Down
4 changes: 2 additions & 2 deletions apps/backend/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async function seed() {
create: {
allowLocalhost: true,
oauthProviderConfigs: {
create: (['github', 'spotify', 'google', 'microsoft'] as const).map((id) => ({
create: (['github', 'spotify', 'google', 'microsoft', 'eartho'] as const).map((id) => ({
id,
proxiedOAuthConfig: {
create: {
Expand Down Expand Up @@ -84,7 +84,7 @@ async function seed() {
create: {}
}
},
...(['github', 'spotify', 'google', 'microsoft'] as const).map((id) => ({
...(['github', 'spotify', 'google', 'microsoft', 'eartho'] as const).map((id) => ({
oauthProviderConfig: {
connect: {
projectConfigId_id: {
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/oauth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import { MicrosoftProvider } from "./providers/microsoft";
import { MockProvider } from "./providers/mock";
import { SpotifyProvider } from "./providers/spotify";
import { XProvider } from "./providers/x";
import { EarthoProvider } from "./providers/eartho";

const _providers = {
github: GithubProvider,
google: GoogleProvider,
facebook: FacebookProvider,
microsoft: MicrosoftProvider,
eartho: EarthoProvider,
spotify: SpotifyProvider,
discord: DiscordProvider,
gitlab: GitlabProvider,
Expand Down
42 changes: 42 additions & 0 deletions apps/backend/src/oauth/providers/eartho.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { OAuthUserInfo, validateUserInfo } from "../utils";
import { OAuthBaseProvider, TokenSet } from "./base";

export class EarthoProvider extends OAuthBaseProvider {
private constructor(
...args: ConstructorParameters<typeof OAuthBaseProvider>
) {
super(...args);
}

static async create(options: {
clientId: string,
clientSecret: string,
}) {
return new EarthoProvider(...await OAuthBaseProvider.createConstructorArgs({
issuer: "https://account.eartho.io",
discoverFromUrl: "https://account.eartho.io/.well-known/openid-configuration",
redirectUri: getEnvVariable("STACK_BASE_URL") + "/api/v1/auth/oauth/callback/eartho",
baseScope: "openid profile email",
noPKCE: false,
openid: true,
...options,
}));
}

async postProcessUserInfo(tokenSet: TokenSet): Promise<OAuthUserInfo> {
const info = await fetch("https://account.eartho.io/api/oidc/userinfo", {
headers: {
Authorization: `Bearer ${tokenSet.accessToken}`,
},
}).then((res) => res.json());

return validateUserInfo({
accountId: info.sub,
displayName: info.name,
email: info.email,
profileImageUrl: info.picture,
emailVerified: info.email_verified,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function PageClient () {
credentialEnabled: form.watch("signInMethods").includes("credential"),
magicLinkEnabled: form.watch("signInMethods").includes("magicLink"),
passkeyEnabled: form.watch("signInMethods").includes("passkey"),
oauthProviders: form.watch('signInMethods').filter((method) => ["google", "github", "microsoft", "spotify"].includes(method)).map(provider => ({ id: provider, type: 'shared' })),
oauthProviders: form.watch('signInMethods').filter((method) => ["google", "github", "microsoft", "eartho"].includes(method)).map(provider => ({ id: provider, type: 'shared' })),
}
};

Expand All @@ -56,7 +56,7 @@ export default function PageClient () {
credentialEnabled: values.signInMethods.includes("credential"),
magicLinkEnabled: values.signInMethods.includes("magicLink"),
passkeyEnabled: values.signInMethods.includes("passkey"),
oauthProviders: (["google", "facebook", "github", "microsoft"] as const).map(provider => ({
oauthProviders: (["google", "facebook", "github", "microsoft", "eartho"] as const).map(provider => ({
id: provider,
enabled: values.signInMethods.includes(provider),
type: 'shared'
Expand Down Expand Up @@ -94,7 +94,7 @@ export default function PageClient () {
{ value: "google", label: "Google" },
{ value: "github", label: "GitHub" },
{ value: "microsoft", label: "Microsoft" },
{ value: "spotify", label: "Spotify" },
{ value: "eartho", label: "Eartho" },
]}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function toTitle(id: string) {
google: "Google",
facebook: "Facebook",
microsoft: "Microsoft",
eartho: "Eartho",
spotify: "Spotify",
discord: "Discord",
gitlab: "GitLab",
Expand Down
3 changes: 2 additions & 1 deletion apps/oauth-mock-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ const mockedProviders = [
"facebook",
"google",
"microsoft",
"eartho",
"spotify",
"discord",
"gitlab",
"bitbucket",
"x",
];

const configuration: Configuration = {
const configuration: Configuration = {
clients: mockedProviders.map((providerId) => ({
client_id: providerId,
client_secret: 'MOCK-SERVER-SECRET',
Expand Down
5 changes: 5 additions & 0 deletions docs/fern/docs/pages/getting-started/production.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ To use your own OAuth provider setups in production, follow these steps for each
Callback URL:
`https://api.stack-auth.com/api/v1/auth/oauth/callback/x`
</Tab>
<Tab title="Eartho">
[Eartho OAuth Setup Guide](https://docs.eartho.io)
Callback URL:
`https://api.stack-auth.com/api/v1/auth/oauth/callback/eartho`
</Tab>
</Tabs>

2. **Enter OAuth Credentials**: Go to the `Auth Methods` section in the Stack dashboard, open the provider's settings, switch from shared keys to custom keys, and enter the client ID and client secret.
Expand Down
4 changes: 2 additions & 2 deletions packages/stack-shared/src/utils/oauth.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const standardProviders = ["google", "github", "microsoft", "spotify", "facebook", "discord", "gitlab", "bitbucket", "linkedin", "apple", "x"] as const;
export const standardProviders = ["google", "github", "microsoft", "eartho", "spotify", "facebook", "discord", "gitlab", "bitbucket", "linkedin", "apple", "x"] as const;
// No more shared providers should be added except for special cases
export const sharedProviders = ["google", "github", "microsoft", "spotify"] as const;
export const sharedProviders = ["google", "github", "microsoft", "eartho"] as const;
export const allProviders = standardProviders;

export type ProviderType = typeof allProviders[number];
Expand Down
28 changes: 28 additions & 0 deletions packages/stack/src/components/oauth-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,24 @@ function XIcon({ iconSize } : { iconSize: number} ) {
);
}

function EarthoIcon({ iconSize }: { iconSize: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height={iconSize}
width={iconSize}
fill="none"
viewBox="0 0 512 512"
>
<rect width="512" height="512" fill="#000" rx="32"></rect>
<path
fill="#fff"
d="M158.284 401V110.091h196.023v50.71H219.79v69.318h124.432v50.711H219.79v69.46h135.085V401z"
></path>
</svg>
);
}

const changeColor = (c: Color, value: number) => {
if (c.isLight()) {
value = -value;
Expand Down Expand Up @@ -278,6 +296,16 @@ export function OAuthButton({
};
break;
}
case 'eartho': {
style = {
backgroundColor: "#fff",
textColor: "#000",
border: '1px solid #ddd',
name: "Eartho",
icon: <EarthoIcon iconSize={iconSize} />,
};
break;
}
default: {
style = {
name: provider,
Expand Down