Sign in with Apple

@David, the author of the PR has responded to my comment, what’s your opinion on this?:

Me:

This PR appears to solve the issue with the lack of name and email?

PR Author:

No, unfortunately not. Apple needs to provide a REST API endpoint to retrieve name/email as they currently only pass this information upon successful authentication once and not on subsequent authentications.

Isn’t one-time at initial Authorisation good enough for us?

It’s better than nothing, but it’s still not good. For example, if you “sign in with apple”, but then click cancel on the “create account” screen. Or if you connect an existing account with apple, then decide to create a new account instead. Hopefully Apple will resolve that before the end of the beta :crossed_fingers:

5개의 좋아요

현재 시점에 Sign in with Apple 기능이 정상적으로 작동하는 구현 예가 있나요? 마켓플레이스 관련 작업을 진행 중인 사이트에서 iOS 앱을 출시할 계획인데, 이 옵션이 없으면 앱 가이드라인 위반으로 앱이 거부되지 않도록 다른 인증 옵션을 활성화할 수 없습니다.

제가 아는 한, 없습니다. 이 문제가 해결되기를 기다리고 있습니다: How to retrieve email and name? · Issue #8 · nhosoya/omniauth-apple · GitHub, 하지만 저장소 소유자가 무성의하게 이슈를 닫아 버렸습니다.

확실하지는 않지만, 이 커밋과 8월의 다른 커밋들로 문제가 해결되지 않았나요?

테스트를 위해 플러그인을 설치해 보셔도 무방하지만, 아직 문제가 해결되었는지에는 대해 잘 모르겠습니다. 개발자가 시간을 내어 확인하고 승인할 때까지, 또는 작업이 완료될 때까지 프로덕션 환경에서 사용하는 것은 권장하지 않습니다.

저는 이 기능에 대한 큰 지지자입니다. 다른 로그인 옵션처럼 바닐라 Discourse에 기본적으로 포함되기를 바랍니다.

저는 의도적으로 그런 정보를 가져올 수 없다고 생각합니다.

앱 또는 웹사이트에 Apple로 로그인 버튼을 표시하면 사용자는 이미 보유한 Apple ID를 사용하여 탭 한 번으로 로그인 또는 가입할 수 있으며, 양식 작성, 이메일 주소 확인, 비밀번호 선택을 생략할 수 있습니다. Apple로 로그인은 사용자가 신뢰할 수 있는 일관된 로그인 경험과 여러 계정과 비밀번호를 기억할 필요가 없는 편의성을 제공하면서, 앱과 웹사이트에 간단하고 빠르게 로그인할 수 있는 새로운 방식의 프라이버시를 제공합니다. 이름과 이메일 주소를 요청하는 경우를 제외하고, 사용자는 이메일 주소를 비공개로 유지하고 대신 고유한 무작위 이메일 주소를 공유할 수 있는 옵션을 가집니다.

Apple의 전체 개발자 아티클 보기

맞습니다. 프라이버시는 Apple 로그인(Sign In with Apple)의 핵심 부분 중 하나이지만, 인용문의 핵심은 다음과 같습니다:

사용자가 이름/이메일을 제공하도록 선택한다면, 사용자가 로그인할 때마다 제공자로부터 해당 정보를 받을 수 있을 것으로 예상합니다. 그러나 현재 구현에서는 이것이 일어나지 않습니다. 첫 번째 인증 이후에는 사용자 정보를 전혀 받지 못합니다.

이것은 게م(gem) 작성자가 수정할 수 있는 문제가 아니라 Apple이 변경해야 할 부분이라고 생각합니다. 가까운 시일 내에 그런 일이 일어날 것 같지 않으므로, 아마도 사용자가 Discourse에서 이름/이메일을 수동으로 입력하도록 요청해야 할 것 같습니다 :cry:

4개의 좋아요

네, 하지만 정보를 제공하지 않기로 선택하는 사람들은 어떻게 해야 합니까?

아, 그리고 여기 거친 개념도 하나 있습니다. :sweat_smile:

좋은 소식입니다:

Robert/merefield의 플러그인 포크를 부분적으로 작동시키는 데 성공했습니다(해당 포크는 GitHub의 최신 소스에서 빌드한 omniauth gem의 사본으로 전환하는 것만 포함합니다). 그러나 제 테스트 Discourse(ngrok을 통해 끝에서 끝까지 HTTPS를 사용하는 환경)에서는 인증이 작동하려면 동일한 사이트 쿠키 Site Setting을 (없음)으로 설정해야 했습니다. 해당 설정을 비활성화하면 계정 생성(가입 양식을 닫았을 경우에도)이 가능하고 다시 로그인할 수도 있지만, 설정이 활성화된 상태에서는 다시 로그인할 수 없습니다.

로그인 실패 시 백트레이스는 다음과 같습니다:

로그인 실패 백트레이스
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:112:in `report_to_store'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:103:in `add_with_opts'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:54:in `add'
/usr/local/lib/ruby/2.6.0/logger.rb:543:in `error'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:163:in `log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:486:in `fail!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-oauth2-1.6.0/lib/omniauth/strategies/oauth2.rb:71:in `callback_phase'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:238:in `callback_call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:189:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/builder.rb:64:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:47:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/tempfile_reaper.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/conditional_get.rb:38:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/head.rb:12:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:318:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/session/abstract/id.rb:259:in `context'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/session/abstract/id.rb:253:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/cookies.rb:648:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:101:in `run_callbacks'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/middleware/reporter.rb:43:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/rack/logger.rb:38:in `call_app'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/rack/logger.rb:28:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:18:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:31:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/method_override.rb:22:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/executor.rb:14:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/sendfile.rb:111:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/host_authorization.rb:77:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.4/lib/mini_profiler/profiler.rb:184:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/message_bus-2.2.3/lib/message_bus/rack/middleware.rb:57:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:181:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/engine.rb:526:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/railtie.rb:190:in `public_send'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/railtie.rb:190:in `method_missing'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:68:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:53:in `each'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:53:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:605:in `process_client'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:700:in `worker_loop'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:548:in `spawn_missing_workers'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:144:in `start'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `load'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `<main>'

여러분 중 이 사이트 설정을 비활성화하지 않아도 플러그인의 핵심 기능이 작동하도록 플러그인을 다시 작성할 수 있는지/어떻게 해야 하는지에 대한 제안을 주실 분이 있을까요? 제 플러그인 코드는 https://github.com/sau226dev/discourse-sign-in-with-apple 에 있으며, omniauth gem을 재빌드하는 데 사용한 최신 코드는 같은 GitHub 조직 내에 있어야 합니다.

미리 도움을 주셔서 감사합니다.

sau226

4개의 좋아요

플러그인은 원래 어느 정도 기능은 했었지만, 위에서 David가 설명한 문제로 인해 최근에는 관리가 이루어지지 않았습니다.

Apple이 그 근본적인 장애 요소(모든 로그인 시도에 이름과 이메일을 전송하는 문제)를 해결했다는 긍정적인 소식이 들어오기까지는 이 코드를 유지보수하는 것이 가치가 없다고 생각합니다(imho). 이 문제를 우회할 수 있는 방법이 있다고 생각하지 않습니다. 그래서 의존성 업데이트조차 시도하지 않았습니다. 어쨌든 플러그인은 테스트를 통과하지 못했을 것입니다.

따라서 이것은 ‘출판된’ 플러그인이 아닙니다(그렇지 않았다면 이 주제나 유사한 주제가 #customization:plugin에 있을 것입니다). 또한 해당 문제가 해결될 때까지 지원이 이루어질 가능성은 낮습니다. 만약 그 문제가 해결되고 Apple이 해당 정보를 제공하게 된다면, 플러그인을 마무리하는 데 기꺼이 참여하겠습니다.

그 외에도 또 하나의 중요한 문제가 있습니다(btw): 자신의 사이트에서 이를 사용하려면 Apple Developer Programme에 비용을 지불하고 가입하여 Apple 시스템 설정에 접근할 수 있어야 합니다. 무료 또는 저렴한 가입이 아니기 때문에, 이는 많은 저예산 사이트들을 주저하게 만들 것으로 예상됩니다.

6개의 좋아요

@sau226 님의 의견에 따르면, 이메일/이름이 반환되지 않는 것이 실제로는 블로커(장애 요인)가 아니라고 보는 것 같습니다.

@orenwolf (이후 로그인 시 이메일/이름이 반환되지 않는 문제)은 문제가 아닌 것 같습니다. 가입 창을 닫은 후 올바른 정보를 전달하여 가입을 재개할 수 있었습니다. 앞서 말씀드린 것처럼, 이후 Apple 로그인도 바로 문제없이 성공했습니다.

제가 겪은 유일한 문제는 위에서 언급한 사이트 설정을 비활성화하지 않으면 CSRF 오류가 발생하는 것이었습니다. 잠재적인 문제로는 로그인 양식에 이름 항목이 없고, 사용자 이름이 이메일의 @ 기호 앞 부분으로 설정되는 점이 있습니다(그러나 제 생각에는 이러한 잠재적 문제는 문제가 되지 않거나 사용자가 쉽게 수정할 수 있습니다).

4개의 좋아요

위에서 David가 남긴 댓글 외에도, Apple 개발자 지원 사이트에서 관련 주제를 발견했는데, 해당 주제에는 이 문제를 확인해 주는 공식 답변이 달려 있었습니다:

공식 답변:

Hi aslkdjalksdjasdasd,
This behaves correctly, user info is only sent in the ASAuthorizationAppleIDCredential upon initial user sign up. Subsequent logins to your app using Sign In with Apple with the same account do not share any user info and will only return a user identifier in the ASAuthorizationAppleIDCredential. It is recommened that you securely cache the initial ASAuthorizationAppleIDCredential containing the user info until you can validate that an account has succesfully been created on your server.
Patrick

한 개발자가 이렇게 댓글을 달았습니다:

잠깐… 만약 Apple로부터의 첫 번째 리다이렉트가 매우 흔한 여러 가지 이유 중 하나로 인해 유실되면, 사용자의 정보를 다시 가져올 다른 방법이 없으므로 해당 사용자를 영구적으로 잃게 되는 건가요? 이 정보를 가져올 다른 방법이 정말 없는 건가요?

또 다른 개발자는 이렇게 말했습니다:

아니면 다운스트림에서 문제가 발생하면 고객들이 불만을 제기하고, 지원팀에서 Apple ID 웹사이트에 들어가서 권한을 철회하도록 안내하여 다시 올바르게 등록할 수 있게 할 것입니다. 이런 종류의 문제가 발생하기 시작하면 이 로그인 메커니즘을 사용하지 않게 될 것이며, 이는 매우 나쁜 경험이 될 것이라고 생각합니다.

따라서 안타깝게도 이 방식을 프로덕션 환경에서 안전하게 사용할 수 없다고 생각합니다. 이건 지원팀의 _지옥_이 될 것입니다.

Apple이 그들이 만든 문제에 눈을 뜰 때까지 이 주제를 잠깐 보류하는 것을 제안합니다. 보안 개선을 시도하는 과정에서 견고성을 지나치게 희생한 것 같습니다.

11개의 좋아요

아쉽네요. :cry:

1개의 좋아요

Apple이 “Sign in with Apple” 개발자 페이지를 업데이트하여 데이터 수집, 데이터 관리 등에 대한 정보를 더 자세히 제공했습니다.
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/data-management/

5개의 좋아요

omniauth-apple gem을 최신 버전으로 업데이트하기 위한 pull request를 열었습니다. 해당 업데이트에는 이 커밋이 포함되어 있으며, 매 로그인 시 사용자 이메일을 받지 못하는 문제를 해결할 수 있을 것으로 보입니다.

이를 테스트하기 위해 권장 블로그 게시물을 따라 자격 증명을 설정했지만, 아직 파악하지 못한 부분이 두 가지 있습니다:

  • Apple에서 서비스 ID를 설정하는 데 필요한 리다이렉트 반환 URL의 경로는 무엇인가요?
  • “verification txt” 파일을 어떻게 가져오나요, 아니면 이것이 필요하지 않나요? 검색을 해보니 도메인/이메일 통신 설정의 일부로 Apple에서 다운로드할 수 있다는 언급을 찾았지만, 더 이상 그런 것 같지는 않습니다:
3개의 좋아요

고맙습니다.

보통은 무언가를 성공적으로 테스트한 후에 PR을 제출하죠 :slight_smile:

완료되면 확인해 주세요.

상식적인 문제인 것 같지만, Apple에서 이제 매번 이메일을 발송한다는 공식 입장을 밝혀주면 더 안심할 수 있을 것 같습니다. Apple이 핵심 문제를 해결하지 않았다면, gem 버전을 변경한다고 해서 해결되지 않을 테니까요.

Apple Developer로 다시 가입할 때 제 설정을 확인해 보겠습니다. 아직 가입을 안 했거든요 … (이번 사태가 좀 위축이 되긴 했습니다, 솔직히 말해서)

@David

플러그인 포크를 최신 omniauth-apple을 사용하도록 업데이트해 보았습니다. (단순히 버전 번호만 올리는 것 외에도 몇 가지 다른 변경 사항이 필요합니다.)

요약하자면: 여전히 깨져 있습니다.

샌드박스 환경에서는 어떻게든 작동하도록 해킹했지만, 여전히 여러 문제가 있습니다:

  1. Apple은 콜백에서 POST 요청을 사용하고 있습니다. OAuth 구현에서 이는 흔하지 않으며, samesite=Lax 쿠키가 요청과 함께 전송되지 않는다는 것을 의미합니다. 즉, Discourse는 콜백 동안 세션 쿠키를 읽을 수 없으므로 CSRF 오류가 발생합니다.

    안전하지 않은 우회 방법은 Discourse 쿠키를 samesite=None으로 변경하여 이 보안 설정을 비활성화하는 것입니다.

  2. CSRF 토큰 없이 POST 요청을 사용하는 것은 코어 내의 또 다른 보안 조치도 트리거합니다.

    안전하지 않은 우회 방법은 이 라인을 제거하는 것입니다.

  3. omniauth 게엠이 JWK를 가져오는 동안 Apple에서 403 오류가 발생했습니다. Accept: 헤더가 올바르게 설정되지 않고 있다고 의심하지만, 아직 확인은 하지 못했습니다.

    안전하지 않은 우회 방법은 키를 하드코딩하는 것이었습니다.

모든 과정을 거친 후, 마침내 Apple로 로그인하는 데 성공했습니다. https://sandbox.dtaylor.uk에서 직접 테스트해 볼 수 있습니다. (몇 일 동안 작동하게 두겠지만, 안전하지 않은 환경이므로 민감한 정보를 입력하지 마세요.)

그런데… 이메일/이름은 여전히 첫 번째 인증 시에만 포함됩니다. 이걸 테스트해 볼 수 있습니다 - Apple로 로그인한 후 계정 생성을 취소하고 다시 시도해 보세요. 두 번째 시도 시에는 사용자 정보가 누락됩니다.

즉, Apple이 당분간 방식을 변경하지 않을 것이라면… 어떻게 하면 이 문제를 해결할 수 있을까요?

(1)과 (2)에 대해서는 보안에 해를 끼치지 않으면서 Apple의 POST 요청을 GET 요청으로 변환할 수 있다고 생각합니다. 콜백에서 POST 요청을 받으면 window.location='/auth/apple/callback?code=...&state=...'을 설정하는 JavaScript를 렌더링합니다. 그러면 그 이후부터는 다른 제공자와 똑같이 작동하게 됩니다. 다만, POST 요청을 가로채려면 코어 API에 몇 가지 변경 사항이 필요할 것 같습니다.

(3)에 대해서는 omniauth 게엠에 약간의 작업을 추가하면 수정될 수 있을 것 같습니다.

하지만 여전히 이름/이메일을 받을 수 없는 상태이므로, 이 다른 문제들을 수정하는 것이 가치가 있는지 확신이 서지 않습니다 :cry:

8개의 좋아요

다시 시도해 주시고 분석해 주셔서 감사합니다! 남은 문제들을 좀 더 명확히 파악할 수 있도록 Apple에 기술 지원 요청을 제출하는 것이 도움이 될 수 있습니다. 제 경험상 Apple 개발자 포럼보다는 기술 지원 채널에서 해결책이나 우회 방법을 더 자주 제공해 줍니다.