Сегодня пользователи могут выбирать из множества поставщиков удостоверений в соответствии со своими потребностями. Sign in with Apple — это последнее дополнение к линейке социальных провайдеров, предлагающее отличные возможности для пользователей Apple.

Функция входа с помощью Apple оптимизирована для бесперебойной работы на всех устройствах Apple; с tvOS на macOS. Пользователи могут использовать знакомые FaceID или TouchID для быстрого и безопасного входа в сторонние приложения. Apple также обещает не отслеживать и не профилировать пользователей и предлагает опцию Скрыть мою электронную почту для тех, кто предпочитает держать свой адрес электронной почты в секрете.

Войти с помощью Apple — это привлекательный вариант входа для многих пользователей Apple. Однако и здесь есть свои нюансы. Давайте рассмотрим некоторые из этих нюансов и то, как они могут повлиять на пользователей сторонних приложений.

Войти с помощью Apple на других платформах

Вход с помощью Apple перенаправляет пользователей на других платформах на веб-портал, где они сначала вводят свои учетные данные Apple ID. Затем пользователи подтверждают код, отправленный на их устройства Apple или в SMS. При желании пользователи могут пропустить этап второго фактора на 30 дней.

На данный момент Apple не поддерживает постоянные сеансы для пользователей, которые входят в систему на других платформах. Как правило, поставщики удостоверений поддерживают сеанс пользователя после того, как пользователь впервые вводит свои учетные данные. Этот сеанс используется при аутентификации в клиентских приложениях, чтобы пользователям не приходилось постоянно повторно вводить свои учетные данные.

Второй фактор Apple работает, либо отправляя код на доверенное устройство Apple, либо отправляя код по SMS. Это ограничение по сравнению с другими социальными провайдерами, которые предлагают более широкий выбор вариантов второго фактора, которые хорошо работают на нескольких платформах. Некоторые даже используют основанные на стандартах вторые факторы, такие как TOTP, в дополнение к проприетарным.

Для многих пользователей iPhone — единственное устройство Apple, которым они владеют. Для этих пользователей потерянный или украденный iPhone не оставит других вариантов доступа к приложениям, которые используют функцию «Войти с Apple». В целом, чем больше у пользователя способов подтвердить свою личность, тем меньше вероятность того, что он потеряет доступ к своей учетной записи.

Итог: вход с помощью Apple — это фантастический опыт в рамках экосистемы Apple. Хотя отсутствие постоянных сеансов и ограниченные параметры второго фактора ухудшают работу на других платформах, потенциально подвергая некоторых пользователей риску блокировки учетной записи.

Дублирующиеся аккаунты

При внедрении функции «Вход с Apple» в существующее приложение существует высокая вероятность того, что многие пользователи, которые ранее пользовались услугами других поставщиков, начнут использовать функцию «Вход с Apple». Когда это происходит, пользователи теряют доступ к данным своей исходной учетной записи. Это может разочаровать пользователя и помешать ему использовать функцию «Войти через Apple» или само приложение. Этот сценарий известен как дублирование пользователей и имеет меньшее отношение к технологии Sign in with Apple, чем к ее статусу начинающего поставщика.

Иногда дублированные пользователи могут быть обнаружены, если учетные записи имеют общий атрибут, например подтвержденный адрес электронной почты. Однако функция «Войти через Apple» дает пользователям возможность скрыть свой адрес электронной почты за псевдонимом, уникальным для каждого приложения, поэтому мы не можем полагаться на этот метод.

Apple отвечает на проблему, интегрируя вход в Apple с помощью iCloud Keychain, пользователям с существующей учетной записью в службе предлагается вместо этого использовать свои учетные данные. Однако Keychain хранит только комбинации электронной почты/имени пользователя и пароля, что не очень полезно в сценариях входа через социальные сети.

В конце концов, пользователям по-прежнему приходится выбирать между преимуществами входа с помощью Apple и сохранением данных существующей учетной записи. Что, если бы они могли делать и то, и другое?

Связывание учетной записи с функцией «Вход с Apple»

Мы только что говорили о том, что вход с помощью Apple представляет трудности для двух групп потенциальных пользователей: мультиплатформенных пользователей и пользователей с существующими учетными записями. Один из способов улучшить взаимодействие с пользователем для обеих групп пользователей — использовать привязку учетных записей.

Когда пользователь связывает две учетные записи, он может использовать любую из учетных записей для доступа к одному и тому же профилю пользователя. Привязка аккаунта может:

  • Предотвращение дублирования учетных записей, поскольку пользователи могут просто связать свои существующие учетные записи с помощью функции «Вход с Apple»;
  • Снизить риск блокировки учетной записи, предоставив пользователям более одного способа доступа к своим учетным записям;
  • Улучшите процесс входа в систему для многоплатформенных пользователей, которые могут использовать функцию «Вход с Apple» на своих устройствах Apple и другого поставщика на своих устройствах сторонних производителей.

Чтобы продемонстрировать, как привязку учетных записей можно интегрировать в существующее приложение, мы создадим настраиваемый процесс входа в систему, который расширит возможности аутентификации, чтобы предложить контекстно-зависимые предложения по привязке учетных записей для пользователей Apple. Для этого мы будем использовать привязку учетной записи Auth0 в сочетании с Правилами Auth0.

Наш поток будет выглядеть так:

Как мы уже обсуждали, привязку учетных записей можно использовать в качестве меры безопасности для снижения риска блокировки учетной записи. Поскольку пользователи склонны придерживаться значений по умолчанию, мы всегда должны предлагать самый безопасный вариант по умолчанию. Это делает процесс входа в систему лучшим временем для предложения привязки учетной записи. Предупреждая пользователей заранее, мы можем предотвратить такие проблемы, как дублирование учетных записей и блокировка, до того, как они возникнут.

Создание приложения для привязки учетной записи

Мы начнем с создания простого веб-приложения, которое аутентифицирует пользователей, а затем попросим пользователей Apple связать другую учетную запись. Это похоже на процесс предлагаемой привязки учетных записей для обработки дубликатов учетных записей, описанный в документации Auth0. В нашем случае, поскольку мы не всегда можем быть уверены, что учетная запись является дубликатом, мы будем запрашивать у всех пользователей Apple, даже если мы не можем обнаружить повторяющуюся учетную запись.

Код нашего приложения будет:

  1. Аутентифицировать пользователя
  2. Определить пользователей Apple
  3. Предложите пользователю Apple войти в систему, используя другую учетную запись
  4. Связать аккаунты

По мере реализации нашего пользовательского потока мы будем разбивать процесс на отдельные примеры кода. В демонстрационных целях наши примеры показывают сокращенную версию кода и не предназначены для использования в исходном виде. Чтобы увидеть полную реализацию, посетите Github.

Настроить аутентификацию

Для аутентификации пользователей мы будем использовать Универсальный вход Auth0. Чтобы настроить это, мы сначала зарегистрируем наше веб-приложение с Auth0. Мы показываем этот процесс в следующем видео:

После того, как мы зарегистрировали наше приложение, мы передаем соответствующие переменные конфигурации в библиотеку passport-auth0, которая обрабатывает аутентификацию в нашем приложении. Библиотека сделает за нас большую часть тяжелой работы.

Код должен выглядеть так:

/** * Import config if .env is present */ import 'dotenv/config'; /** * Import express and base utilities */ import Express from 'express'; import cookieSession from 'cookie-session'; import bodyParser from 'body-parser'; /** * Import auth0 and passport helpers */ import passport from 'passport'; import Auth0 from 'passport-auth0'; import { ensureLoggedIn } from 'connect-ensure-login'; const app = Express(); const IS_PROD = process.env.NODE_ENV === 'production'; /** * Setup rendering engine */ app.set('view engine', 'pug'); app.set('trust proxy'); /** * Configure sessions, we use sessions * in order to parse the body */ app.use( cookieSession({ name: 'session', secret: process.env.COOKIE_SECRET, // cookie options maxAge: 24 * 60 * 60 * 1000, // enable these when we are no longer using localhost: httpOnly: IS_PROD, secure: IS_PROD }) ); /** * Inject bodyParser */ app.use( bodyParser.urlencoded({ extended: true }) ); // Configure Passport to use Auth0 var auth0 = new Auth0( { domain: process.env.AUTH0_DOMAIN, clientID: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, callbackURL: process.env.BASE_URL + '/callback' }, function(accessToken, refreshToken, extraParams, profile, done) { return done(null, profile); } ); passport.use(auth0); app.use(passport.initialize()); app.use(passport.session()); // Passport session setup. passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(obj, done) { done(null, obj); }); /** * Complete authentication */ app.get('/callback', function(req, res, next) { passport.authenticate('auth0', function(err, user, info) { if (err) { return next(err); } if (!user) { return res.redirect('/login'); } req.logIn(user, function(err) { if (err) { return next(err); } const returnTo = req.session.returnTo; delete req.session.returnTo; res.redirect(returnTo || '/'); }); })(req, res, next); }); /** * Route for passport */ app.get( '/login', passport.authenticate('auth0', { scope: 'openid profile email' }) ); /** * Ensure everything beyond this point has a session */ app.use( ensureLoggedIn({ redirectTo: '/login' }) ); app.use((req, res) => res.end('Hi ' + req.user.displayName));

Идентификация пользователей Apple и подсказки им

Как мы уже говорили, наше приложение перенаправляет пользователей Apple на страницу, где их просят связать другую учетную запись. Мы идентифицируем пользователей Apple как пользователей, которые:

  • вход с помощью Войти с Apple,
  • или войти в систему с iOS или Mac OS через поставщика, не принадлежащего Apple.

В первом случае мы просим пользователя связать аккаунт с другим провайдером (в нашем демо-приложении мы показываем только логин Google, но наш подход будет работать с любым количеством провайдеров).

Во втором случае мы предлагаем связать учетную запись Apple пользователя, чтобы улучшить его опыт входа в систему на устройствах Apple. Хотя многие пользователи Apple предпочтут входить в систему со своими учетными записями в социальных сетях вместо Apple ID, они все равно выиграют от привязки учетных записей, поскольку функция «Войти с Apple» предлагает отличный способ входа на всех устройствах Apple.

import useragent from 'useragent'; app.use((req, res, next) => { req.ua = useragent.parse(req.get('User-Agent')); next(); }); app.get('/start', (req, res) => { const { user } = req; if (user.provider === 'apple') { return res.redirect('/prompt/apple'); } else { const os = req.ua.os.family; if (os === 'Mac OS X' || os === 'iOS') { return res.redirect('/prompt/google') } } return res.end('Error Unknown provider'); }); app.get('/prompt/apple', ensureProvider('apple'), (req, res) => { res.render('apple', { ...req.user }); }); app.get('/prompt/google', ensureProvider('google-oauth2'), (req, res) => { res.render('google', { ...req.user }); });

Сохранение пользователя для привязки

Как только пользователь соглашается связать свою учетную запись, он может немедленно войти в систему со своим вторым провайдером. Но прежде чем мы перенаправим их на страницу входа, нам нужно сохранить идентификатор пользователя в нашем сеансе, чтобы позже мы могли связать две учетные записи. Мы можем добиться этого с помощью следующего кода:

app.get('/connect/:provider', (req, res, next) => { if (req.session.identities && req.session.identities.length > 0) { return next(new Error('Invalid step, linking already in progress')); } const userid = req.user.id; const { provider } = req.params; req.session.identities = [userid]; req.session.returnTo = '/connect/done' req.session.expectedProvider = provider; passport.authenticate('auth0', { connection: provider, prompt: 'login', scope: 'openid profile email' })(req, res, next); });

Связать аккаунты

Чтобы связать учетные записи, нашему приложению требуется доступ к API управления Auth0 с областями действия read:users и update:users. В следующем видео показано, как это сделать с панели управления Auth0:

Используя Node.js sdk Auth0, мы создаем клиент управления в приложении, чтобы связать учетные записи пользователей:

import { ManagementClient } from 'auth0'; const management = new ManagementClient({ domain: process.env.AUTH0_DOMAIN, clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET }); app.get('/connect/done', async (req, res) => { if (!req.session.identities && req.session.identities.length < 1) { return next(new Error('Callback cannot be called without first Id')); } if (!req.session.expectedProvider) { return next(new Error('Callback cannot be called without expected provider')); } if (req.user.provider !== req.session.expectedProvider) { return next(new Error('Callback cannot be called without expected provider')); } const userid = req.user.id; req.session.identities.push(userid); const [primaryUserId, secondaryUserId] = req.session.identities; const [secondaryConnectionId, ...rest] = secondaryUserId.split('|'); const secondaryUserProviderId = rest.join('|'); try { await management.linkUsers(primaryUserId, { user_id: secondaryUserProviderId, provider: secondaryConnectionId }); res.render('final', { primaryUser: primaryUserId, secondaryUser: secondaryUserId }); } catch (e) { next(e); } });

Интеграция приложения для привязки учетной записи с потоком входа с использованием правил Auth0

Теперь, когда мы можем связать учетные записи пользователей с нашим веб-приложением для привязки учетных записей, как мы можем интегрировать его в существующий процесс входа в систему? Как мы увидим, расширяемость Auth0 позволяет нам легко расширять поток аутентификации по умолчанию, предоставляемый универсальным входом Auth0. Все, что нам нужно сделать, это перенаправить в наше приложение после аутентификации.

Наш расширенный опыт выглядит так:

  1. Пользователь аутентифицируется в исходном приложении
  2. Если пользователь является пользователем Apple, перенаправить в связывающее приложение, отправив все соответствующие данные
  3. Выполнить привязку аккаунта
  4. Перенаправить обратно в исходное приложение, завершив транзакцию аутентификации.

«Клей», соединяющий наше исходное приложение с нашим компоновщиком, — это правила Auth0; пользовательские функции, выполняемые Auth0 как часть конвейера аутентификации. Правила выполняются после завершения аутентификации, но до того, как Auth0 отправит пользователя обратно в приложение. В результате они являются отличным инструментом для расширения процесса аутентификации.

В нашем случае мы хотим перенаправить из исходного приложения в связывающее приложение после того, как мы определили, что пользователь является пользователем Apple. Мы реализуем это с помощью двух правил: первое определяет, должен ли пользователь быть перенаправлен, а второе — выполняет фактическое перенаправление. На этом этапе мы также можем добавить любые данные, необходимые приложению компоновщика.

Обнаружение пользователя Apple

Как и в нашем приложении компоновщика, наше первое правило проверяет поставщика удостоверений пользователя и платформу, чтобы определить, является ли он пользователем Apple. Их можно найти в объекте контекст, переданном в правила.

Если мы обнаруживаем пользователя Apple, мы устанавливаем новое логическое свойство shouldLink. Мы добавляем это свойство в объект контекста, чтобы указать нашему следующему правилу, что мы должны перенаправить в связывающее приложение.

function (user, context, callback) { const useragent = require('useragent'); context.shouldLink = false; let agent = useragent.parse(context.request.userAgent); if (context.connection === 'apple') { context.shouldLink = true; } else { if (agent.os.family === 'Mac OS X' || agent.os.family === 'iOS') { context.shouldLink = true; } } console.log('User is linkable?', context.shouldLink); callback(null, user, context); }

Перенаправление пользователя с помощью правил

Правила можно использовать для перенаправления пользователя на новую страницу перед завершением транзакции аутентификации. Все, что нам нужно сделать, это установить для свойства context.redirect.url URL-адрес нашего приложения компоновщика.

function (user, context, callback) { if (context.shouldLink && context.clientID !== configuration.APPLE_LINK_CLIENT_ID) { console.log('Should redirect'); context.redirect = { url: configuration.APPLE_LINK_APP_URI }; } return callback(null, user, context); }

Мы также проверяем значение идентификатора клиента — это идентификатор клиента, запрашивающего Auth0 для получения информации об аутентифицирующемся пользователе. Как мы помним, наше приложение компоновщика начинается с запроса Auth0 для аутентификации пользователя. Однако в этот момент пользователь уже войдет в систему Auth0, поскольку наши правила перенаправляют в приложение после аутентификации.

Почему это важно? Когда мы просим Auth0 аутентифицировать пользователя, у которого уже есть сеанс Auth0, поток аутентификации не запускается, но правила остаются. Поэтому, чтобы избежать бесконечного цикла, нам нужно убедиться, что пользователь перенаправляется только тогда, когда запрос исходит не от нашего приложения компоновщика.

Изменения в нашем приложении для интеграции с Правилами

Интеграция нашего приложения компоновщика с правилами требует некоторых незначительных изменений. Когда мы перенаправляем наше приложение компоновщика из правил, Auth0 отправляет параметр состояния в URL-адресе. После завершения процесса привязки учетной записи нам нужно отправить параметр состояния обратно в Auth0 в конечной точке /continue. Это перенаправит нас обратно к Auth0 и завершит транзакцию аутентификации. Оборачиваем эту логику в следующий код:

app.get('/start', (req, _, next) => { const { query } = req; const { state } = query; if (!state) { return next(new Error('Unauthorized `state` is missing')); } if (req.session.auth0State) { return next(new Error('Only one instance of redirect should run at a time')); } req.session.auth0State = state; next(); }); // Redirect rule handler app.use((req, res, next) => { const { session } = req; const { auth0State } = session; res.redirectCallback = (queryParams = {}) => { queryParams.state = auth0State; const queryStr = Object.entries(queryParams) .map(x => x.join('=')) .join('&'); const urlStr = `https://${process.env.AUTH0_DOMAIN}/continue?${queryStr}`; req.session = null; res.redirect(urlStr); }; next(); });

Вывод

Войти с помощью Apple — это отличный новый способ взаимодействия с пользователями для компаний. Однако принятие провайдера создает проблемы для новых и существующих приложений, таких как обработка дублирования учетных записей и обеспечение удобного взаимодействия с пользователем на нескольких платформах.

Мы показали, как можно использовать расширяемость Auth0, чтобы предлагать контекстно-зависимую учетную запись, связанную с пользователями во время процесса входа в систему. В то время как наше демонстрационное приложение использует простой алгоритм для обнаружения пользователей Apple, в реальных приложениях им доступен гораздо больший контекст, который можно использовать для улучшения эвристики.

Что вы думаете о контекстной привязке учетных записей? Вы бы использовали это в своем приложении? Комментарий ниже.

О Auth0

Auth0, платформа идентификации для разработчиков приложений, предоставляет тысячам клиентов в каждом секторе рынка единственное решение для идентификации, необходимое им для их веб-приложений, мобильных приложений, IoT и внутренних приложений. Его расширяемая платформа легко аутентифицирует и защищает более 2,5 миллиардов входов в систему в месяц, что делает ее любимой разработчиками и надежной компанией со всего мира. Штаб-квартира компании в США в Белвью, штат Вашингтон, и дополнительные офисы в Буэнос-Айресе, Лондоне, Токио и Сиднее обслуживают своих глобальных клиентов, которые находятся в более чем 70 странах.

Для получения дополнительной информации посетите https://auth0.com или подпишитесь на @auth0 в Twitter.

Первоначально опубликовано на https://auth0.com.