Loading...
Azure-Ad-B2C-Mod-Data
Author Cloudapp
E.G.

So manipulieren Sie Ihre Azure AD B2C-Daten über Ihre REST-API

28. Mai 2024
Inhaltsverzeichnis

In den vorherigen Beiträgen habe ich einen Azure AD B2C-Tenant erstellt und eine Schritt-für-Schritt-Anleitung für die Integration in Ihr Next.js 14-Projekt gegeben. Darauf aufbauend habe ich zunächst das Styling der Anmelde-/Registrierseite geändert und schließlich drei Identitätsanbieter (Google, GitHub und Microsoft) zum AD B2C-Tenant hinzugefügt. Hier ist das GitHub-Repo mit dem vollständigen Code

Voraussetzungen:

Prisma Anpassungen

Als ersten Schritt werden wir zwei neue Tabellen zu unserem Prisma-Schema unter prisma/prisma.schema hinzufügen. Wir werden diese neuen Tabellen verwenden, um das Zugriffstoken zu speichern, das wir von Azure AD erhalten, sowie die neuen Rollen.

model ApiToken {
  tokenId        String   @id @default(cuid())
  accessToken    String   @db.VarChar
  tokenType      String   @default("Azure")
  expirationDate DateTime
  addedOn        DateTime @default(now())
}

model roles {
  id      Int      @id @default(autoincrement())
  name    String
  addedOn DateTime @default(now())
}

Nun führen wir

npx prisma db push

aus, damit die Änderungen an die PostgresDB übertragen werden.

Hier die vollständige prisma.schema Datei (Tabelle “logins” haben wir im letzten Post last post regarding custom login data tracking hinzugefügt).

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "debian-openssl-1.1.x"]
}

datasource db {
  provider     = "postgresql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

model logins {
  id        Int      @id @default(autoincrement())
  email     String
  name      String
  logins    Int
  objectId  String?
  lastLogin DateTime @default(now())
}

model ApiToken {
  tokenId        String   @id @default(cuid())
  accessToken    String   @db.VarChar
  tokenType      String   @default("Azure")
  expirationDate DateTime
  addedOn        DateTime @default(now())
}

model roles {
  id      Int      @id @default(autoincrement())
  name    String
  addedOn DateTime @default(now())
}

Neue Umgebungsvariablen

Wir werden die fehlenden Umgebungsvariablen zu unserer .env.local-Datei hinzufügen.

# Getting Data from Azure AD B2C
AZURE_CLIENT_ID=xxxx
AZURE_CLIENT_SECRET=xxxx
AZURE_GRANT_TYPE=client_credentials
AZURE_SCOPE=https://graph.microsoft.com/.default
AZURE_TENANT_ID=xxxx
AZURE_TOKEN_URL=https://login.microsoftonline.com/xxx/oauth2/v2.0/token
AZURE_B2C_EXTENSION_USER=extension_277fdddaa2e54130a3832d63736a808a_Role

Melden Sie sich unter https://portal.azure.com/#view/Microsoft_AAD_B2CAdmin/TenantManagementMenuBlade/~/registeredApps an und holen sich die Schlüssel.

Klicken Sie auf die App-Registrierung, die Sie für die Produktionsumgebung erstellt haben. In meinem Fall „Testprod“. Auf der entsprechenden Übersichtsseite können Sie die „Anwendungs-ID (Client-ID)“ für die Umgebungsvariable „AZURE_CLIENT_ID“ abrufen, und Sie können den „AZURE_CLIENT_SECRET“ im Abschnitt „Zertifikate & Geheimnisse“ auf der linken Seite erhalten. Die „AZURE_TENANT_ID“ ist ebenfalls in der Übersicht der App-Registrierung unter „Verzeichnis-ID (Mandanten-ID)“ sichtbar.

Verwenden Sie die AZURE_TENANT_ID auch für die „AZURE_TOKEN_URL“ und ersetzen Sie das „xxxx“ in der URL durch die Mandanten-ID.

AZURE_TOKEN_URL=https://login.microsoftonline.com/xxx/oauth2/v2.0/token
Azure-AD-B2C-Overview
Azure-AD-B2C-Overview

Benuterdefinierte Attribute in AZURE AD B2C

Für die letzte Umgebungsvariable „AZURE_B2C_EXTENSION_USER“ klicken wir auf den entsprechenden Eintrag „b2c-extensions-app.xxxx“ und kopieren die „Anwendungs-ID (Client-ID)“. Dann entfernen wir alle Bindestriche in der ID und fügen „extension_(ID ohne Bindestriche)_custom_attribute“ zu einem String zusammen. Aber halt, was ist „Role“ in diesem Fall?

AZURE_B2C_EXTENSION_USER=extension_xxxx_Role

„Role“ ist das benutzerdefinierte Attribut, das wir während der Mandantenerstellung hinzugefügt haben.

Wir benötigen es, um die „Custom Attribute“-Daten aus dem AD-Verzeichnis abzurufen. Aber ein wichtiges Detail fehlt. Wir müssen die Zugriffsrechte für den Benutzer „Testprod“ ändern, damit wir auf die AD-Verzeichnisdaten zugreifen können.

Request-API-permissions-Microsoft-Azure
Request-API-permissions-Microsoft-Azure
Azure-Ad-B2C-Grant-Admin-Consent
Azure-Ad-B2C-Grant-Admin-Consent

Neue Api Route für Access Token

Lassen Sie uns eine neue API-Route hinzufügen, um zuerst das Access Token und dann die Benutzer im AD-Verzeichnis abzurufen. Wir verwenden Prisma, um das Access Token in der PostgreSQL-Datenbank zu speichern, sodass wir nur ein neues Token abrufen, wenn das alte abgelaufen ist. Also erstellen wir eine neue Route unter src/app/api/azure/user.

import type { AuthTokenResp } from "@/types/api";
import prisma from "@/lib/prisma";
import { unixTimestampToDate, urlBase64Decode } from "@/lib/utils";
import { NextResponse, NextRequest } from "next/server";
import { getToken } from "next-auth/jwt";

async function AzureToken() {
  const formdata = new FormData();

  formdata.append("grant_type", "client_credentials");
  formdata.append("client_id", process.env.AZURE_CLIENT_ID || "");
  formdata.append("client_secret", process.env.AZURE_CLIENT_SECRET || "");
  formdata.append("scope", process.env.AZURE_SCOPE || "");

  if (!process.env.AZURE_TOKEN_URL) {
    throw new Error("AZURE_TOKEN_URL is not set");
  }

  const res = await fetch(process.env.AZURE_TOKEN_URL, {
    method: "POST",
    body: formdata,
  });

  if (!res.ok) {
    const text = await res.text(); // get the response body for more information

    throw new Error(`
      Failed to fetch data
      Status: ${res.status}
      Response: ${text}
    `);
  }

  const newTokens: AuthTokenResp = await res.json();

  return newTokens;
}

async function AzureData() {
  const mostRecentApiToken = await prisma.apiToken.findFirst({
    where: { tokenType: "Azure" },
    orderBy: {
      addedOn: "desc",
    },
  });

  let token: string = "";

  if (mostRecentApiToken) {
    const { accessToken, expirationDate } = mostRecentApiToken;

    const tokenExpirationDate = new Date(expirationDate);
    const currentDate = new Date();

    token = accessToken;

    // compare the current date to the expiration date
    if (currentDate > tokenExpirationDate) {
      //Get Access Token
      const tokenazure: any = await AzureToken();
      const newTokens = tokenazure;

      const accessToken = newTokens.access_token;
      const { exp, nbf } = urlBase64Decode(accessToken.split(".")[1]);
      const newExpirationDate = unixTimestampToDate(exp);

      await prisma.apiToken.create({
        data: {
          accessToken: accessToken,
          expirationDate: newExpirationDate,
          tokenType: "Azure",
          addedOn: new Date(),
        },
      });

      //Accesstoken der Variablen zuweisen
      token = accessToken;
    }
  } else {
    //Get Access Token
    const tokenazure: any = await AzureToken();
    const newTokens = tokenazure;

    const accessToken = newTokens.access_token;
    const { exp, nbf } = urlBase64Decode(accessToken.split(".")[1]);
    const newExpirationDate = unixTimestampToDate(exp);

    await prisma.apiToken.create({
      data: {
        accessToken: accessToken,
        expirationDate: newExpirationDate,
        tokenType: "Azure",
        addedOn: new Date(),
      },
    });

    //Accesstoken der Variablen zuweisen
    token = accessToken;
  }

  return token;
}

export async function GET(req: NextRequest) {
  const authtoken = await getToken({ req });
  const isAdmin = authtoken?.role === "Admin";
  if (!authtoken || !isAdmin) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  let data = null;

  //Get Access Token
  const token = await AzureData();

  const res = await fetch(
    `https://graph.microsoft.com/beta/users?$select=displayName,id,identities,createdDateTime,otherMails,${process.env.AZURE_B2C_EXTENSION_USER}`,
    {
      method: "GET",
      cache: "no-cache",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + token,
      },
    }
  );
  data = await res.json();

  return NextResponse.json({
    data,
  });
}

Wir benötigen zwei weitere Routen zum „Patchen“ oder Aktualisieren der Azure AD-Daten und zum Hinzufügen von „role“-Einträgen zu unseren Rollentabellen in PostgreSQL.

import type { AuthTokenResp } from "@/types/api";
import prisma from "@/lib/prisma";
import { unixTimestampToDate, urlBase64Decode } from "@/lib/utils";
import { NextResponse, NextRequest } from "next/server";
import { getToken } from "next-auth/jwt";

async function AzureToken() {
  const formdata = new FormData();

  formdata.append("grant_type", "client_credentials");
  formdata.append("client_id", process.env.AZURE_CLIENT_ID || "");
  formdata.append("client_secret", process.env.AZURE_CLIENT_SECRET || "");
  formdata.append("scope", process.env.AZURE_SCOPE || "");

  if (!process.env.AZURE_TOKEN_URL) {
    throw new Error("AZURE_TOKEN_URL is not set");
  }

  const res = await fetch(process.env.AZURE_TOKEN_URL, {
    method: "POST",
    body: formdata,
  });

  if (!res.ok) {
    const text = await res.text(); // get the response body for more information

    throw new Error(`
      Failed to fetch data
      Status: ${res.status}
      Response: ${text}
    `);
  }

  const newTokens: AuthTokenResp = await res.json();

  return newTokens;
}

async function AzureData() {
  const mostRecentApiToken = await prisma.apiToken.findFirst({
    where: { tokenType: "Azure" },
    orderBy: {
      addedOn: "desc",
    },
  });

  let token: string = "";

  if (mostRecentApiToken) {
    const { accessToken, expirationDate } = mostRecentApiToken;

    const tokenExpirationDate = new Date(expirationDate);
    const currentDate = new Date();

    token = accessToken;

    // compare the current date to the expiration date
    if (currentDate > tokenExpirationDate) {  
      //Get Access Token
      const tokenazure: any = await AzureToken();
      const newTokens = tokenazure; 

      const accessToken = newTokens.access_token;
      const { exp, nbf } = urlBase64Decode(accessToken.split(".")[1]);
      const newExpirationDate = unixTimestampToDate(exp);   

      await prisma.apiToken.create({
        data: {
          accessToken: accessToken,
          expirationDate: newExpirationDate,
          tokenType: "Azure",
          addedOn: new Date(),
        },
      });

      //Accesstoken
      token = accessToken;

    }
  } else {
    //Get Access Token
    const tokenazure: any = await AzureToken();
    const newTokens = tokenazure;

    const accessToken = newTokens.access_token;
    const { exp, nbf } = urlBase64Decode(accessToken.split(".")[1]);  
    const newExpirationDate = unixTimestampToDate(exp);

    await prisma.apiToken.create({
      data: {
        accessToken: accessToken,
        expirationDate: newExpirationDate,
        tokenType: "Azure",
        addedOn: new Date(),
      },
    });

    //Accesstoken
    token = accessToken;

  }

  return token;
}

export async function POST(req: NextRequest) {
  // Extract the `messages` from the body of the request
  const { id, role } = await req.json();

  const AZURE_B2C_EXTENSION = `{${process.env.AZURE_B2C_EXTENSION_USER}: '${role}'}`;
  const azurerole = JSON.stringify(AZURE_B2C_EXTENSION);

  const authtoken = await getToken({ req });
  const isAdmin = authtoken?.role === "Admin";
  if (!authtoken || !isAdmin) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  //Get Access Token
  const token = await AzureData();

  const patchUrl = "https://graph.microsoft.com/v1.0/users/" + id;

  const res = await fetch(patchUrl, {
    method: "PATCH",
    body: AZURE_B2C_EXTENSION,
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + token,
    },
  });

  return new NextResponse(undefined, {
    status: res.status,
    statusText: res.statusText,
  });
}

Rollen hinzufügen

import prisma from "@/lib/prisma";
import { getToken } from "next-auth/jwt";
import { NextResponse, NextRequest } from "next/server";

export async function POST(req: NextRequest) {
  const authtoken = await getToken({ req });
  const isAdmin = authtoken?.role === "Admin";
  if (!authtoken || !isAdmin) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  // Extract the `messages` from the body of the request
  const { name } = await req.json();

  let data: any = await prisma.roles.findFirst({
    where: { name: name },
  });

  if (data) {
    data["status"] = "present";
  }

  if (!data) {
    data = await prisma.roles.create({
      data: {
        name: name,
      },
    });
    data["status"] = "new";
  }

  return NextResponse.json({
    data,
  });
}

Neue NPM Pakete

Wir benötigen drei neue NPM-Pakete für die neuen Frontend-Komponenten.

npm i @tremor/react react-icons react-toastify

Anpassung Tailwind.config.js

Für Styling-Zwecke werden wir die tailwind.config.ts-Datei anpassen.

import type { Config } from "tailwindcss";
const { fontFamily } = require("tailwindcss/defaultTheme");

const config: Config = {
  darkMode: "class",
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./node_modules/@tremor/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    // extraColors,
    extend: {
      colors: {
        // ...extraColors,
        // light mode
        tremor: {
          brand: {
            faint: "#eff6ff", // blue-50
            muted: "#bfdbfe", // blue-200
            subtle: "#60a5fa", // blue-400
            DEFAULT: "#3b82f6", // blue-500
            emphasis: "#1d4ed8", // blue-700
            inverted: "#ffffff", // white
          },
          background: {
            muted: "#f9fafb", // gray-50
            subtle: "#f3f4f6", // gray-100
            DEFAULT: "#ffffff", // white
            emphasis: "#374151", // gray-700
          },
          border: {
            DEFAULT: "#e5e7eb", // gray-200
          },
          ring: {
            DEFAULT: "#e5e7eb", // gray-200
          },
          content: {
            subtle: "#9ca3af", // gray-400
            DEFAULT: "#6b7280", // gray-500
            emphasis: "#374151", // gray-700
            strong: "#111827", // gray-900
            inverted: "#ffffff", // white
          },
        },
        // dark mode
        "dark-tremor": {
          brand: {
            faint: "#0B1229", // custom
            muted: "#172554", // blue-950
            subtle: "#1e40af", // blue-800
            DEFAULT: "#3b82f6", // blue-500
            emphasis: "#60a5fa", // blue-400
            inverted: "#030712", // gray-950
          },
          background: {
            muted: "#131A2B", // custom
            subtle: "#1f2937", // gray-800
            DEFAULT: "#111827", // gray-900
            emphasis: "#d1d5db", // gray-300
          },
          border: {
            DEFAULT: "#374151", // gray-700
          },
          ring: {
            DEFAULT: "#1f2937", // gray-800
          },
          content: {
            subtle: "#4b5563", // gray-600
            DEFAULT: "#6b7280", // gray-600
            emphasis: "#e5e7eb", // gray-200
            strong: "#f9fafb", // gray-50
            inverted: "#000000", // black
          },
        },
      },
      maxWidth: {
        "8xl": "90rem",
      },
      letterSpacing: {
        snug: "-0.011em",
      },
      boxShadow: {
        // light
        "tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
        "tremor-card":
          "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
        "tremor-dropdown":
          "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
      },
      borderRadius: {
        "tremor-small": "0.375rem",
        "tremor-default": "0.5rem",
        "tremor-full": "9999px",
      },
      fontSize: {
        "2xs": "0.625rem",
        "3xl": "1.75rem",
        "4xl": "2.5rem",
        "tremor-label": "0.75rem",
        "tremor-default": ["1.0rem", { lineHeight: "1.25rem" }],
        "tremor-title": ["1.125rem", { lineHeight: "1.75rem" }],
        "tremor-metric": ["1.875rem", { lineHeight: "2.25rem" }],
      },
      lineHeight: {
        tighter: "1.1",
      },
      fontFamily: {
        sans: ["var(--font-urbanist)", ...fontFamily.sans],
      },
    },
  },
  plugins: [require("@tailwindcss/typography")],
};
export default config;

Wir haben eine neue Zeile nach Zeile 7 hinzugefügt, die notwendig ist, damit Tailwind auf die benötigten Tremor-Dateien zugreifen kann.

 "./node_modules/@tremor/**/*.{js,ts,jsx,tsx,mdx}",

und wir haben einen neuen „Colorblock“ unter Zeile 13 hinzugefügt

colors: {      
        // light mode
        tremor: {
          brand: {
            faint: "#eff6ff", // blue-50
            muted: "#bfdbfe", // blue-200
            subtle: "#60a5fa", // blue-400
            DEFAULT: "#3b82f6", // blue-500
            emphasis: "#1d4ed8", // blue-700
            inverted: "#ffffff", // white
          },
          background: {
            muted: "#f9fafb", // gray-50
            subtle: "#f3f4f6", // gray-100
            DEFAULT: "#ffffff", // white
            emphasis: "#374151", // gray-700
          },
          border: {
            DEFAULT: "#e5e7eb", // gray-200
          },
          ring: {
            DEFAULT: "#e5e7eb", // gray-200
          },
          content: {
            subtle: "#9ca3af", // gray-400
            DEFAULT: "#6b7280", // gray-500
            emphasis: "#374151", // gray-700
            strong: "#111827", // gray-900
            inverted: "#ffffff", // white
          },
        },
        // dark mode
        "dark-tremor": {
          brand: {
            faint: "#0B1229", // custom
            muted: "#172554", // blue-950
            subtle: "#1e40af", // blue-800
            DEFAULT: "#3b82f6", // blue-500
            emphasis: "#60a5fa", // blue-400
            inverted: "#030712", // gray-950
          },
          background: {
            muted: "#131A2B", // custom
            subtle: "#1f2937", // gray-800
            DEFAULT: "#111827", // gray-900
            emphasis: "#d1d5db", // gray-300
          },
          border: {
            DEFAULT: "#374151", // gray-700
          },
          ring: {
            DEFAULT: "#1f2937", // gray-800
          },
          content: {
            subtle: "#4b5563", // gray-600
            DEFAULT: "#6b7280", // gray-600
            emphasis: "#e5e7eb", // gray-200
            strong: "#f9fafb", // gray-50
            inverted: "#000000", // black
          },
        },
      },

Dann erstellen wir drei neue page.tsx-Dateien unter src/app/[locale]/dashboard, src/app/[locale]/dashboard/activities und src/app/[locale]/dashboard/user, da wir den Backend-Teil für eingeloggte Benutzer mit Admin-Rechten erstellen werden. Die Änderungen in den common.json-Dateien sind notwendig, um die Beschriftungs-/Schaltflächentexte zu übersetzen. Einen vollständigen Überblick finden Sie im verbundenen GitHub-Repository, das Sie hier finden.

New Files
New Files

Wir fügen eine neue Typdatei (src/types/api.ts) für die Daten hinzu, die von der Azure Rest API kommen, und korrigieren die Typdatei für next-Auth (src/types/next-auth.ts). Als letzten Schritt fügen wir eine neue Datei unter src/lib für Base64-Codierung usw. hinzu.

Utils.ts

import { DecodedPayload } from "@/types/api";

const unixTimestampToDate = (unixTimestamp: number): Date => {
  return new Date(unixTimestamp * 1000);
};

const urlBase64Decode = (payload: string): DecodedPayload => {
  // decode the base64 string
  const base64Decoded = atob(payload);

  // URL-decode the data
  const urlDecoded = decodeURIComponent(base64Decoded);

  // parse the JSON data
  return JSON.parse(urlDecoded);
};

export { unixTimestampToDate, urlBase64Decode };

und wir passen den Name des neuen benutzerdefinierten Attributes in der Datei src/lib/auth.ts an.

Konfiguration der Rollen in Azure AD B2C

Da die Rollentabelle neu ist, haben wir noch keine Daten darin. Nur ein Benutzer mit dem Wert „Admin“ im Rollenattribut kann nach dem Einloggen den Dashboard-Bereich sehen und darauf zugreifen. Wie können wir das lösen?

// src/app/[locale]/dashboard
  const session = await getServerSession(authOptions);
  const user = session?.user;
  const role = session?.role;

  if (role !== "Admin") {
    return (
      <>
        <div>Not authenticated...</div>
      </>
    );
  }

Wir müssen diese Informationen zu Azure AD B2C übertragen, da wir sie nicht über die Web-Oberfläche unter https://portal.azure.com hinzufügen können. Sie können Postman verwenden, aber ich bevorzuge die VSCode-Erweiterung „Thunder Client“ für diese Aufgabe, da sie bereits in meine IDE VSCode integriert ist.

Um es einfacher zu machen, werde ich drei verschiedene Anfragen erstellen:

Access Token generieren

Request type: POST Url: https://login.microsoftonline.com/xxx/oauth2/v2.0/token (replace xxx with tour tenant ID) Body: “Form” auswählen und 4 Felder hinzufügen (grant_type, client_id, client_secret, scope). Die entsprechenden Werte der Felder können der Datei .env.local entnommen werden.

UserID aus User Objekt holen

Request type: GET Url: https://graph.microsoft.com/beta/users?$select=displayName,id,identities,createdDateTime,otherMails,extension_xxxx_Role (replace xxx with the ClientID of the corresponding b2c_extension_user of your AD) Auth: “Bearer” auswählen und den Access Token aus der ersten Abfrage einsetzen. id aus Json Antwort holen.

  {
      "displayName": "Test Test",
      "id": "xxx-xxx-xxx-xxx-xxx",
      "createdDateTime": "2024-05-17T15:40:50Z",
      "otherMails": [],
      "extension_xxx_Role": "Admin",
      "identities": [
    {
          "signInType": "emailAddress",
          "issuer": "xxx.onmicrosoft.com",
          "issuerAssignedId": "xx.xxx@example.com"
        },
        {
          "signInType": "userPrincipalName",
          "issuer": "xxx.onmicrosoft.com",
          "issuerAssignedId": "xxxx"
        }
      ]
    },

User Objekt "Patchen" und neuen Wert für das benutzerdefinierte Attribute "Role" einfügen

Request type: PATCH Url: https://graph.microsoft.com/v1.0/users/xxx (xxx mit der UserID aus der vorherigen Abfrage ersetzen) Auth: “Bearer” auswählen und den Access Token aus der ersten Abfrage einsetzen. Body:

{
  "extension_xxxx_Role": "Admin"
}

Nach dieser Patch-Anfrage wird das Benutzerobjekt den neuen Wert anzeigen, und Sie können versuchen, sich mit diesem Benutzer anzumelden, um zu sehen, ob Sie das neue Dashboard sehen können.

Hier das Endresultat

Ein neues Dropdown-Menü mit dem Menüpunkt „Dashboard“, falls der Benutzer „Admin“-Rechte hat.

Example-Blog-Dropdown
Example-Blog-Dropdown

Hier die “Aktivitäten” Seite

Example-Blog-Logindata
Example-Blog-Logindata
Example-Blog-Roles-Users
Example-Blog-Roles-Users

Zuerst müssen Sie die benötigten Rollen wie „Admin“, „Editor“ oder was auch immer Sie bevorzugen, erstellen und dann den Benutzern zuweisen. Sobald Sie auf den CTA „Rolle ändern“ klicken, wird das Benutzerobjekt in Azure AD aktualisiert.

Cloudapp-dev und bevor Sie uns verlassen

Danke, dass Sie bis zum Ende gelesen haben. Noch eine Bitte bevor Sie gehen:

Wenn Ihnen gefallen hat was Sie gelesen haben oder wenn es Ihnen sogar geholfen hat, dann würden wir uns über einen "Clap" 👏 oder einen neuen Follower auf unseren Medium Account sehr freuen.

Verwandte Artikel

Azure-Ad-B2C-Logs

Tracking der Logindaten mit Prisma/Serverless Postgres in Next.js 14

Ich möchte Anmeldungen nachverfolgen, um Benutzer bei Problemen zu unterstützen und um besser zu verstehen, was bei der Anmeldung passiert. Azure bietet eine intelligente Möglichkeit hierfür

Author Cloudapp
E.G.
25. Mai 2024
Azure-Ad-B2C-ID-Provider

Hinzufügen von Identitätsanbietern (Google, Github und Microsoft) zu Azure AD B2C Tenant, um Social Login zu ermöglichen

Wir werden uns auf mögliche Identitätsanbieter für unseren Azure B2C Tenant konzentrieren, da wir jedes Hindernis für unsere Benutzer während des Anmeldevorgangs beseitigen möchten. Lassen Sie uns mit den wichtigsten beginnen.

Author Cloudapp
E.G.
23. Mai 2024
Azure-Ad-B2C

Einfache Integration von Azure AD B2C in Next.js 14

Wir integrieren nun den AD B2C Mandanten, in unser Next.js 14 Projekt. Als Basis dient uns dabei das Repo, welches wir in den letzten Posts erstellt haben.

Author Cloudapp
E.G.
18. Mai 2024
Azure-Ad-styling

Styling der Azure AD B2C Signup-Signin Seite Next.js 14

Nach der Integration in unser Next.js 14 Projekt, werden wir die Anmelde- und Registrierungsseite, die von Azure AD B2C bereitgestellt wird, grafisch anpassen.

Author Cloudapp
E.G.
21. Mai 2024
Azure-Ad-B2C

Azure AD B2C: Integrieren Sie eine der besten Authentifizierungs- und Autorisierungsplattformen kostenlos in Ihr Next.js 14 Projekt

Ein Teil fehlt noch für unser vollwertiges Blog-Projekt auf der Basis von Next.js 14, Contentful und Algolia. Und das ist die Benutzerregistrierung und Anmeldung (Authentifizierung und Autorisierung), die heutzutage unerlässlich ist. Wir werden AD B2C verwenden.

Author Cloudapp
E.G.
17. Mai 2024