diff --git a/docs/2-run-locally.md b/docs/2-run-locally.md index f8f59c244..5a587f567 100644 --- a/docs/2-run-locally.md +++ b/docs/2-run-locally.md @@ -1,29 +1,37 @@ # 👨🏻‍💻 Run Locally -Clone this repository locally or fork to your GitHub account. Run all of the the steps below from the `src` directory. +Clone this repository locally or fork to your GitHub account. Follow the steps below to run the solution locally: ## Prerequisites -- **History Database**: If you don't want to [provision the Azure resources](./4-deploy-to-azure.md), you **must** at least deploy an instance of Azure Cosmos DB in your Azure Subscription to store chat history. +Azure Chat is heavily dependant on a large number of Azure services. The easiest way to deploy all of these required services into an Azure subscription is to use the the [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview) as follows: -- **Identity Provider**: For local development, you have the option of using a username / password to sign in. If you prefer to use an Identity Provider, follow the [instructions](./3-add-identity.md) in the next chapter to add one. +1. Install the [Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview) +1. From the root of the repository: -## Steps to Run Locally + 1. Run `azd init` + 1. Run `azd provision` to provision the Azure resources + + +## Identity Provider + +For local development you can use the `Basic Auth (DEV ONLY)` provider to sign in - this local development identity provider will accept any user name and password, and the username you enter will create a new user id (hash of username@localhost) so you can simulate multiple users. If you prefer to use an Identity Provider (Entra ID or GitHub) for local development follow the [instructions](./3-add-identity.md) in the next chapter to add one. + +## Run the App + +With the prerequisites complete, follow the steps below to run the solution locally: 1. Change directory to the `src` folder -2. Rename/copy the file `.env.example` to `.env.local` and populate the environment variables based on the deployed resources in Azure. +2. Rename/copy the file `.env.example` to `.env` and populate the environment variables based on the deployed resources in Azure. - > **NOTE** - > If you have used the Azure Developer CLI to deploy the Azure infrastructure required for the solution ([using the directions here](./4-deploy-to-azure.md)), you can find the values for many the required environment variables in the `.env` file the `.azure\` directory. This generated file will not contain any keys, however it is recommended to use managed identities as described in "Setup your local development environment" on [this page](./9-managed-identities.md). + > **NOTE** + > If you have used the Azure Developer CLI to deploy the Azure services required for the solution (as described above), you can find the values for most the required environment variables in the `.env` file the `.azure\` directory. This generated file will not contain any keys, however it is recommended to use managed identities as described in "Run Locally with Managed Identities" on [this page](./9-managed-identities.md). 3. Install npm packages by running `npm install` 4. Start the app by running `npm run dev` 5. Access the app on [http://localhost:3000](http://localhost:3000) -You should now be prompted to log in with your chosen authentication method (per the pre-requisite configuration). - -> **NOTE** -> If using Basic Auth (DEV ONLY), any username you enter will create a new user id (hash of username@localhost). You can use this to simulate multiple users. Once successfully logged in, you can start creating new conversations. +You should now be prompted to log in with your chosen authentication method (per your Identity Provider configuration), and you can start chatting. ## Continue to the next step... diff --git a/docs/9-managed-identities.md b/docs/9-managed-identities.md index be82cfcae..e5d1ebd04 100644 --- a/docs/9-managed-identities.md +++ b/docs/9-managed-identities.md @@ -40,15 +40,31 @@ Using Managed Identities is preferred for production deployments due to: ### Deploy to Azure with Managed Identities -To deploy the application to Azure App Service with Managed Identities, follow the standard deployment instructions available in the [Deploy to Azure - GitHub Actions](https://github.com/microsoft/azurechat) section of the repository. Ensure to: +To deploy the application to Azure App Service with Managed Identities, follow the standard deployment instructions available in the [Deploy to Azure - GitHub Actions](https://github.com/microsoft/azurechat) section of the repository as follows: 1. **Update the Parameter**: - Set the parameter `disableLocalAuth` to `true` in [`infra/main.bicep`](/infra/main.bicep) (or [`infra/main.json`](/infra/main.json) for ARM deployment) to use Managed Identities. 2. **Deploy resources using azd**: - Refer to the [README](../README.md) -3. **(Optional) Setup your local development environment**: + +## Run Locally with Managed Identities - Run this script to grant yourself RBAC permissions on the Azure resources so you can run AzureChat locally with managed identities. +You can run Azure Chat locally with Managed Identities - in this case the identity of the currently logged in user (via `az login`) is used to authenticate with the required Azure services. Follow the steps below to run Azure Chat locally with Managed Identities: + +1. Refer to the documentation in [Run Locally](2-run-locally.md) to set up your local environment up for development. +1. Update your `.env` file with the following setting: + ``` + USE_MANAGED_IDENTITIES=true + ``` +1. Make sure that your `.env` either has the following settings removed, uncommented, or set to empty. Even though you have set `USE_MANAGED_IDENTITIES=true` the various SDKs that the application uses to interact with these services can still default to key based authentication if these are present: + ``` + AZURE_OPENAI_API_KEY= + AZURE_OPENAI_DALLE_API_KEY= + AZURE_COSMOSDB_KEY= + AZURE_SEARCH_API_KEY= + AZURE_DOCUMENT_INTELLIGENCE_KEY= + ``` +1. Run this script to grant yourself RBAC permissions on the various Azure resources used by Azure Chat. If you haven't already done so then you will need to login to Azure using the Azure CLI command `az login` - In Powershell: @@ -60,7 +76,7 @@ To deploy the application to Azure App Service with Managed Identities, follow t > chmod +x .\scripts\add_localdev_roles.sh > .\scripts\add_localdev_roles.sh ``` - You can now refer to the documentation to [run Azure Chat locally](2-run-locally.md). + ## Conclusion diff --git a/src/features/auth-page/auth-api.ts b/src/features/auth-page/auth-api.ts index 1769caa72..751bc65dd 100644 --- a/src/features/auth-page/auth-api.ts +++ b/src/features/auth-page/auth-api.ts @@ -4,6 +4,8 @@ import CredentialsProvider from "next-auth/providers/credentials"; import GitHubProvider from "next-auth/providers/github"; import { Provider } from "next-auth/providers/index"; import { hashValue } from "./helpers"; +import { image } from "@markdoc/markdoc/dist/src/schema"; +import { access } from "fs"; const configureIdentityProvider = () => { const providers: Array = []; @@ -18,10 +20,13 @@ const configureIdentityProvider = () => { clientId: process.env.AUTH_GITHUB_ID!, clientSecret: process.env.AUTH_GITHUB_SECRET!, async profile(profile) { + const image = await fetchProfilePicture(profile.avatar_url, null); const newProfile = { ...profile, isAdmin: adminEmails?.includes(profile.email.toLowerCase()), + image: image, }; + console.log("GitHub profile:", newProfile); return newProfile; }, }) @@ -38,17 +43,24 @@ const configureIdentityProvider = () => { clientId: process.env.AZURE_AD_CLIENT_ID!, clientSecret: process.env.AZURE_AD_CLIENT_SECRET!, tenantId: process.env.AZURE_AD_TENANT_ID!, - async profile(profile) { + authorization: { + params: { + scope: "openid profile User.Read", + }, + }, + async profile(profile, tokens) { const email = profile.email || profile.preferred_username || ""; + const image = await fetchProfilePicture(`https://graph.microsoft.com/v1.0/me/photos/48x48/$value`, tokens.access_token); const newProfile = { ...profile, email, - // throws error without this - unsure of the root cause (https://stackoverflow.com/questions/76244244/profile-id-is-missing-in-google-oauth-profile-response-nextauth) id: profile.sub, isAdmin: adminEmails?.includes(profile.email?.toLowerCase()) || adminEmails?.includes(profile.preferred_username?.toLowerCase()), + image: image, }; + console.log("Azure AD profile:", newProfile); return newProfile; }, }) @@ -94,6 +106,30 @@ const configureIdentityProvider = () => { return providers; }; +export const fetchProfilePicture = async (profilePictureUrl: string, accessToken: any): Promise => { + console.log("Fetching profile picture..."); + var image = null + const profilePicture = await fetch( + profilePictureUrl, + accessToken && { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + if (profilePicture.ok) { + console.log("Profile picture fetched successfully."); + const pictureBuffer = await profilePicture.arrayBuffer(); + const pictureBase64 = Buffer.from(pictureBuffer).toString("base64"); + image = `data:image/jpeg;base64,${pictureBase64}`; + } + else { + console.error("Failed to fetch profile picture:", profilePictureUrl, profilePicture.statusText); + } + return image; +}; + + export const options: NextAuthOptions = { secret: process.env.NEXTAUTH_SECRET, providers: [...configureIdentityProvider()],