Реализация Social Login агрегации (множество провайдеров) на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Social Login агрегации (множество провайдеров) на сайте
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Агрегация социальных логинов (Multiple OAuth Providers)

Поддержка нескольких OAuth-провайдеров одновременно: пользователь выбирает Google, GitHub, Apple или Microsoft — и попадает в один аккаунт. Ключевой вопрос — связывание аккаунтов при одинаковом email.

NextAuth.js / Auth.js: несколько провайдеров

// auth.ts (Auth.js v5)
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';
import Apple from 'next-auth/providers/apple';
import MicrosoftEntraID from 'next-auth/providers/microsoft-entra-id';

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
    Apple({
      clientId: process.env.APPLE_ID!,
      clientSecret: process.env.APPLE_SECRET!, // JWT из .p8 ключа
    }),
    MicrosoftEntraID({
      clientId: process.env.AZURE_AD_CLIENT_ID!,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
      tenantId: process.env.AZURE_AD_TENANT_ID!, // или 'common' для всех
    }),
  ],

  callbacks: {
    async signIn({ user, account, profile }) {
      // Автоматическое связывание по email
      if (user.email) {
        const existingUser = await db.user.findUnique({
          where: { email: user.email }
        });

        if (existingUser) {
          // Проверяем, не привязан ли уже этот провайдер
          const existingAccount = await db.account.findFirst({
            where: {
              userId: existingUser.id,
              provider: account!.provider,
            }
          });

          if (!existingAccount) {
            // Привязываем новый провайдер к существующему аккаунту
            await db.account.create({
              data: {
                userId: existingUser.id,
                provider: account!.provider,
                providerAccountId: account!.providerAccountId,
                type: account!.type,
                access_token: account!.access_token,
                refresh_token: account!.refresh_token,
                expires_at: account!.expires_at,
              }
            });
          }
          return true;
        }
      }
      return true;
    },

    async session({ session, token }) {
      if (token.sub) {
        session.user.id = token.sub;
      }
      return session;
    },
  },

  adapter: PrismaAdapter(db),
});

Prisma Schema: связанные аккаунты

model User {
  id            String    @id @default(cuid())
  email         String    @unique
  name          String?
  image         String?
  createdAt     DateTime  @default(now())
  accounts      Account[]
  sessions      Session[]
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

UI: кнопки провайдеров

// components/SocialLoginButtons.tsx
'use client';
import { signIn } from 'next-auth/react';

const PROVIDERS = [
  {
    id: 'google',
    name: 'Google',
    icon: <GoogleIcon />,
    className: 'bg-white border border-gray-300 hover:bg-gray-50',
  },
  {
    id: 'github',
    name: 'GitHub',
    icon: <GitHubIcon />,
    className: 'bg-gray-900 text-white hover:bg-gray-800',
  },
  {
    id: 'apple',
    name: 'Apple',
    icon: <AppleIcon />,
    className: 'bg-black text-white hover:bg-gray-900',
  },
  {
    id: 'microsoft-entra-id',
    name: 'Microsoft',
    icon: <MicrosoftIcon />,
    className: 'bg-[#00a4ef] text-white hover:bg-[#0090d4]',
  },
] as const;

export function SocialLoginButtons({
  callbackUrl = '/',
  mode = 'login',
}: {
  callbackUrl?: string;
  mode?: 'login' | 'register';
}) {
  return (
    <div className="flex flex-col gap-3">
      {PROVIDERS.map((provider) => (
        <button
          key={provider.id}
          type="button"
          onClick={() => signIn(provider.id, { callbackUrl })}
          className={`flex items-center gap-3 px-4 py-2.5 rounded-lg font-medium ${provider.className}`}
        >
          {provider.icon}
          <span>{mode === 'login' ? 'Войти' : 'Зарегистрироваться'} через {provider.name}</span>
        </button>
      ))}
    </div>
  );
}

Clerk: готовое решение

// Clerk настраивается через dashboard.clerk.com
// Включаем провайдеры: Google, GitHub, Apple, Microsoft
// Связывание аккаунтов — автоматически

// Использование
import { SignIn } from '@clerk/nextjs';

export default function LoginPage() {
  return (
    <SignIn
      appearance={{
        elements: {
          socialButtonsBlockButton: 'border border-gray-200',
        }
      }}
    />
  );
}

Clerk автоматически обрабатывает conflict resolution при одинаковых email от разных провайдеров, показывая пользователю диалог подтверждения.

Управление привязанными аккаунтами

// Страница настроек: список привязанных провайдеров
import { auth } from '@/auth';
import { db } from '@/lib/db';

export default async function SecuritySettingsPage() {
  const session = await auth();
  const accounts = await db.account.findMany({
    where: { userId: session!.user.id }
  });

  return (
    <div>
      <h2>Связанные аккаунты</h2>
      {accounts.map(account => (
        <div key={account.id} className="flex justify-between">
          <span>{account.provider}</span>
          {accounts.length > 1 && (
            <button onClick={() => unlinkAccount(account.id)}>
              Отвязать
            </button>
          )}
        </div>
      ))}
    </div>
  );
}

Настройка Auth.js с 4 провайдерами, связыванием аккаунтов и Prisma адаптером — 2–3 рабочих дня.