Skip to content
Merged
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
3 changes: 2 additions & 1 deletion frontend/app/sign-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ export default async function SignInPage(props: {
if (url.pathname === "/" || url.pathname === "") {
callbackUrl = "/onboarding";
}
} catch {}
} catch { }
}

return (
<SignIn
enableCredentials={isFeatureEnabled(Feature.EMAIL_AUTH)}
enableGithub={isFeatureEnabled(Feature.GITHUB_AUTH)}
enableGoogle={isFeatureEnabled(Feature.GOOGLE_AUTH)}
enableAzure={isFeatureEnabled(Feature.AZURE_AUTH)}
callbackUrl={callbackUrl}
/>
);
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/sign-up/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ export default async function SignUpPage(props: {
if (url.pathname === "/" || url.pathname === "") {
callbackUrl = "/onboarding";
}
} catch {}
} catch { }
}

return (
<SignUp
enableCredentials={isFeatureEnabled(Feature.EMAIL_AUTH)}
enableGithub={isFeatureEnabled(Feature.GITHUB_AUTH)}
enableGoogle={isFeatureEnabled(Feature.GOOGLE_AUTH)}
enableAzure={isFeatureEnabled(Feature.AZURE_AUTH)}
callbackUrl={callbackUrl}
/>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/assets/logo/microsoft.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions frontend/components/auth/azure-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import Image from "next/image";

import azure from "@/assets/logo/microsoft.svg";
import { Button, type ButtonProps } from "@/components/ui/button";
import { IconSpinner } from "@/components/ui/icons";
import { cn } from "@/lib/utils";

interface AzureButtonProps extends ButtonProps {
text?: string;
isLoading?: boolean;
isDisabled?: boolean;
}

export function AzureButton({
text = "Continue with Microsoft",
className,
onClick,
isLoading,
isDisabled,
...props
}: AzureButtonProps) {
return (
<Button
variant="light"
onClick={onClick}
disabled={isDisabled || isLoading}
className={cn("text-[16px] py-6 px-4 pr-6 w-full", className)}
{...props}
>
<div className="h-5 w-5">
{isLoading ? (
<IconSpinner className="animate-spin" />
) : (
<Image src={azure} alt="Azure Icon" width={20} height={20} />
)}
</div>
<div className="ml-2">{text}</div>
</Button>
);
}
2 changes: 0 additions & 2 deletions frontend/components/auth/github-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import { cn } from "@/lib/utils";

interface GitHubSignInButtonProps extends ButtonProps {
text?: string;
callbackUrl: string;
isLoading?: boolean;
isDisabled?: boolean;
}

export function GitHubButton({
text = "Continue with GitHub",
callbackUrl,
className,
onClick,
isLoading,
Expand Down
2 changes: 0 additions & 2 deletions frontend/components/auth/google-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import { cn } from "@/lib/utils";

interface GoogleButtonProps extends ButtonProps {
text?: string;
callbackUrl: string;
isLoading?: boolean;
isDisabled?: boolean;
}

export function GoogleButton({
text = "Continue with Google",
callbackUrl,
className,
onClick,
isLoading,
Expand Down
22 changes: 16 additions & 6 deletions frontend/components/auth/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { signIn } from "next-auth/react";
import React, { useState } from "react";

import logo from "@/assets/logo/logo.svg";
import { AzureButton } from "@/components/auth/azure-button";
import { EmailSignInButton } from "@/components/auth/email-sign-in";
import { GitHubButton } from "@/components/auth/github-button";
import { GoogleButton } from "@/components/auth/google-button";
Expand All @@ -16,14 +17,15 @@ interface SignInProps {
callbackUrl: string;
enableGoogle?: boolean;
enableGithub?: boolean;
enableAzure?: boolean;
enableCredentials?: boolean;
}

type Provider = "github" | "google";
type Provider = "github" | "google" | "azure-ad";

const defaultErrorMessage = `Failed to sign in. Please try again.`;

const SignIn = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }: SignInProps) => {
const SignIn = ({ callbackUrl, enableGoogle, enableGithub, enableAzure, enableCredentials }: SignInProps) => {
const searchParams = useSearchParams();
const [error, setError] = useState(searchParams.get("error"));
const [isLoading, setIsLoading] = useState<Provider | string>("");
Expand Down Expand Up @@ -58,7 +60,6 @@ const SignIn = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
onClick={() => handleSignIn("google")}
isLoading={isLoading === "google"}
isDisabled={!!isLoading}
callbackUrl={callbackUrl}
/>
)}
{enableGithub && (
Expand All @@ -69,7 +70,16 @@ const SignIn = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
className={cn({
"w-full": enableCredentials,
})}
callbackUrl={callbackUrl}
/>
)}
{enableAzure && (
<AzureButton
onClick={() => handleSignIn("azure-ad")}
isLoading={isLoading === "azure-ad"}
isDisabled={!!isLoading}
className={cn({
"w-full": enableCredentials,
})}
/>
)}
{error && <span className="text-destructive text-xs mt-4">{defaultErrorMessage}</span>}
Expand All @@ -85,11 +95,11 @@ const SignIn = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
{!enableCredentials && (
<div className="text-sm font-medium text-white/70">
By continuing you agree to our{" "}
<a href="https://docs.lmnr.ai/policies/privacy-policy" target="_blank" className="text-white">
<a href="/policies/privacy" target="_blank" className="text-white">
Privacy Policy
</a>{" "}
and{" "}
<a href="https://docs.lmnr.ai/policies/terms-of-service" target="_blank" className="text-white">
<a href="/policies/terms" target="_blank" className="text-white">
Terms of Service
</a>
</div>
Expand Down
22 changes: 16 additions & 6 deletions frontend/components/auth/sign-up.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { signIn } from "next-auth/react";
import React, { useState } from "react";

import logo from "@/assets/logo/logo.svg";
import { AzureButton } from "@/components/auth/azure-button";
import { EmailSignInButton } from "@/components/auth/email-sign-in";
import { GitHubButton } from "@/components/auth/github-button";
import { GoogleButton } from "@/components/auth/google-button";
Expand All @@ -15,14 +16,15 @@ interface SignUpProps {
callbackUrl: string;
enableGoogle?: boolean;
enableGithub?: boolean;
enableAzure?: boolean;
enableCredentials?: boolean;
}

type Provider = "github" | "google";
type Provider = "github" | "google" | "azure-ad";

const defaultErrorMessage = `Failed to sign in. Please try again.`;

const SignUp = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }: SignUpProps) => {
const SignUp = ({ callbackUrl, enableGoogle, enableGithub, enableAzure, enableCredentials }: SignUpProps) => {
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState<Provider | string>("");

Expand Down Expand Up @@ -56,7 +58,6 @@ const SignUp = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
onClick={() => handleSignUp("google")}
isLoading={isLoading === "google"}
isDisabled={!!isLoading}
callbackUrl={callbackUrl}
/>
)}
{enableGithub && (
Expand All @@ -67,7 +68,16 @@ const SignUp = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
className={cn({
"w-full": enableCredentials,
})}
callbackUrl={callbackUrl}
/>
)}
{enableAzure && (
<AzureButton
onClick={() => handleSignUp("azure-ad")}
isLoading={isLoading === "azure-ad"}
isDisabled={!!isLoading}
className={cn({
"w-full": enableCredentials,
})}
/>
)}
{error && <span className="text-destructive text-xs mt-4">{defaultErrorMessage}</span>}
Expand All @@ -83,11 +93,11 @@ const SignUp = ({ callbackUrl, enableGoogle, enableGithub, enableCredentials }:
{!enableCredentials && (
<div className="text-sm font-medium text-white/70">
By continuing you agree to our{" "}
<a href="https://docs.lmnr.ai/policies/privacy-policy" target="_blank" className="text-white">
<a href="/policies/privacy" target="_blank" className="text-white">
Privacy Policy
</a>{" "}
and{" "}
<a href="https://docs.lmnr.ai/policies/terms-of-service" target="_blank" className="text-white">
<a href="/policies/terms" target="_blank" className="text-white">
Terms of Service
</a>
</div>
Expand Down
10 changes: 10 additions & 0 deletions frontend/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import jwt from "jsonwebtoken";
import type { NextAuthOptions, User } from "next-auth";
import AzureADProvider from "next-auth/providers/azure-ad";
import CredentialsProvider from "next-auth/providers/credentials";
import GithubProvider from "next-auth/providers/github";
import GoogleProvider from "next-auth/providers/google";
Expand Down Expand Up @@ -28,6 +29,15 @@ const getProviders = () => {
clientSecret: process.env.AUTH_GOOGLE_SECRET!,
}),
},
{
feature: Feature.AZURE_AUTH,
provider: () =>
AzureADProvider({
clientId: process.env.AUTH_AZURE_AD_CLIENT_ID!,
clientSecret: process.env.AUTH_AZURE_AD_CLIENT_SECRET!,
tenantId: process.env.AUTH_AZURE_AD_TENANT_ID!,
}),
},
{
feature: Feature.EMAIL_AUTH,
provider: () =>
Expand Down
9 changes: 9 additions & 0 deletions frontend/lib/features/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const enum Feature {
SEND_EMAIL = "SEND_EMAIL",
GITHUB_AUTH = "GITHUB_AUTH",
GOOGLE_AUTH = "GOOGLE_AUTH",
AZURE_AUTH = "AZURE_AUTH",
EMAIL_AUTH = "EMAIL_AUTH",
WORKSPACE = "WORKSPACE",
SUPABASE = "SUPABASE",
Expand Down Expand Up @@ -30,6 +31,10 @@ export const isFeatureEnabled = (feature: Feature) => {
return !!process.env.AUTH_GITHUB_ID && !!process.env.AUTH_GITHUB_SECRET;
}

if (feature === Feature.AZURE_AUTH) {
return !!process.env.AUTH_AZURE_AD_CLIENT_ID && !!process.env.AUTH_AZURE_AD_CLIENT_SECRET && !!process.env.AUTH_AZURE_AD_TENANT_ID;
}

if (feature === Feature.FULL_BUILD) {
const environment = process.env.ENVIRONMENT;
if (!environment) {
Expand All @@ -45,5 +50,9 @@ export const isFeatureEnabled = (feature: Feature) => {
);
}

if (feature === Feature.SEND_EMAIL) {
return !!process.env.RESEND_API_KEY;
}

return process.env.ENVIRONMENT === "PRODUCTION";
};