새로온 개발자 센터
11단계 시작하기22단계 연결· 내 현황문서

이 문서의 큐

  1. 왜 필요한가
  2. 붙이기
  3. CLI
  4. MCP
  5. 선언 항목
  6. 무엇을 하고 무엇을 안 하나
  7. 안전 규칙
  8. 확인
  9. Apple 로그인으로 붙일 때

개요

시작하기

Quickstart

기능 붙이기

FormsMembersBoardsShopCatalogsGroupsHoldings블로그 가져오기

앱 붙이기

앱 백엔드 붙이기앱 콜백 브리지

레퍼런스

속성 계약 (data-saeroon-*)CLI 레퍼런스 (@saeroon/cli)APIMCP예약 경로

마켓플레이스

Marketplace

문서 / 앱 붙이기

이 문서의 큐
  1. 왜 필요한가
  2. 붙이기
  3. CLI
  4. MCP
  5. 선언 항목
  6. 무엇을 하고 무엇을 안 하나
  7. 안전 규칙
  8. 확인
  9. Apple 로그인으로 붙일 때

앱 콜백 브리지

앱을 가진 사이트가 웹 로그인 결과를 앱으로 되돌려받는 경로를 엽니다.

경로 하나를 선언하면 그 주소가 POST 를 받아, 받은 폼 필드를 그대로 쿼리스트링으로 옮겨 담아 앱 딥링크로 302 합니다. 페이지에 붙일 코드는 없습니다 — 플랫폼이 그 경로를 직접 답합니다.

왜 필요한가

로그인 제공자 중에는 결과를 요청 본문에 담아 POST 로 보내는 쪽이 있습니다(response_mode=form_post). 값이 본문에 있으니 정적 페이지의 JS 가 location.search 로 읽는 흔한 수법이 원리적으로 통하지 않습니다.

가장 자주 만나는 경우가 안드로이드의 Apple 로그인입니다. Apple 은 안드로이드용 네이티브 API 를 제공하지 않아 앱이 Chrome Custom Tab 에서 웹 흐름을 타는데, Apple 이 개발자가 지정한 HTTPS 주소로 POST 를 보내고 그 응답이 딥링크로의 302 여야 제어가 앱으로 돌아옵니다.

같은 모양을 요구하는 제공자라면 Apple 이 아니어도 이 기능으로 붙습니다.

붙이기

CLI

npx @saeroon/cli app-callback attach \
  --site <siteId> \
  --path /auth/apple/callback \
  --android-package app.example

응답에 제공자 콘솔에 등록할 절대 URL 이 함께 나옵니다.

  /auth/apple/callback  android_intent
    등록할 URL : https://example.saeroon.com/auth/apple/callback
    딥링크     : intent://callback?…#Intent;package=app.example;scheme=signinwithapple;end

list 로 지금 열려 있는 경로를 보고, remove --path <경로> 로 닫습니다.

MCP

saeroon_define_app_callback
  { "path": "/auth/apple/callback", "target": "android_intent", "android_package": "app.example" }

action 은 attach(기본) · list · remove 입니다.

선언 항목

항목기본값설명
path—브리지가 응답할 사이트 내 절대 경로. 쿼리·프래그먼트 불가
target / kindandroid_intentandroid_intent = intent://…#Intent;…;end · custom_scheme = myapp://…
androidPackage—android_intent 필수. 인텐트를 받을 앱 패키지명
schemesigninwithapple딥링크 스킴. 플러그인 규약을 그대로 쓰면 기본값이면 됩니다
callbackPathcallback딥링크의 authority
allowGetfalseGET 도 릴레이할지

사이트당 최대 5개까지 선언할 수 있습니다.

무엇을 하고 무엇을 안 하나

하는 일은 옮겨 담아 튕기는 것뿐입니다.

POST https://example.saeroon.com/auth/apple/callback
Content-Type: application/x-www-form-urlencoded

code=…&id_token=…&state=…&user=…
302 Found
Location: intent://callback?code=…&id_token=…&state=…&user=…#Intent;package=app.example;scheme=signinwithapple;end
Cache-Control: no-store
  • 받은 필드를 전부 옮깁니다. 골라 담지 않습니다 — 제공자가 필드를 늘려도 그대로 통과합니다.
  • error 도 넘어갑니다. 사용자가 취소하면 error=user_cancelled_authorize 가 오는데, 이걸 떨어뜨리면

앱은 에러를 받는 게 아니라 아무것도 못 받고 멈춥니다.

  • 응답 본문은 없습니다. 앱은 HTTP 응답을 읽지 않고 딥링크만 받습니다.

안 하는 일: 토큰 검증, 코드 교환, 회원 조회, 쿠키 설정. 검증은 앱이 백엔드에 보내는 자체 로그인 호출에서 이미 일어납니다. 두 곳에서 다른 규칙이 돌면 그게 인증 버그의 산실입니다.

안전 규칙

목적지는 선언에서만 옵니다. 요청 파라미터는 쿼리스트링으로 옮겨질 뿐, 어디로 튕길지를 한 글자도 바꾸지 못합니다. 폼에 package=com.other.app 을 실어 보내도 그 값은 그냥 쿼리 항목 하나가 됩니다.

웹으로 되튀는 스킴은 저장되지 않습니다. scheme 이 http·https·javascript·data·intent 등이면 선언 자체를 거부합니다. intent://x#Intent;scheme=https;…;end 는 안드로이드에서 브라우저를 여는 인텐트라, 그 자리를 열어 두면 오픈 리다이렉트가 됩니다.

GET 은 기본으로 꺼져 있습니다. POST 는 자격증명이 본문에 있어 요청 URL 이 깨끗하지만, GET 은 code·id_token 이 요청 URL 에 실려 액세스 로그에 남습니다. form_post 제공자는 항상 POST 이므로 켤 일이 거의 없습니다. 정말 필요한 제공자에만 --allow-get 을 씁니다.

선언하지 않은 경로는 그대로입니다. 브리지를 안 붙인 주소의 POST 는 종전처럼 405 이고, GET 정적 서빙은 아무것도 바뀌지 않습니다.

확인

curl -i -X POST https://example.saeroon.com/auth/apple/callback \
  -d 'code=CODE123&id_token=TOK456&state=ST789'

302 와 Location 헤더가 나오면 됩니다. 안드로이드 기기 없이 여기까지 확인할 수 있습니다.

Apple 로그인으로 붙일 때

  1. Apple Developer 에서 Services ID 를 만들고 도메인을 등록·검증합니다.

검증 파일(apple-developer-domain-association.txt)을 배포 폴더의 .well-known/ 에 넣어 함께 배포하면 그 주소로 서빙됩니다.

  1. Return URL 에 위에서 받은 등록할 URL 을 그대로 붙여 넣습니다. Apple 은 바이트 단위로 대조하니

끝의 슬래시 하나도 다르면 안 됩니다.

  1. 앱의 리다이렉트 URI 상수를 같은 값으로 맞춥니다.
  2. 앱은 받은 id_token 을 백엔드 소셜 로그인 엔드포인트로 보내 세션을 받습니다 —

앱 백엔드 붙이기 의 소셜 로그인 절을 그대로 씁니다.

Apple 의 사용자 식별자(sub)는 개발자 팀 단위로 발급됩니다. 그래서 iOS 의 네이티브 흐름과 안드로이드의 이 웹 흐름이 같은 사람에게 같은 값을 주고, 두 기기에서 같은 계정으로 들어옵니다.

요약

앱을 가진 사이트가 웹 로그인 결과를 앱으로 되돌려받는 경로를 엽니다.

마크다운 원문/docs/app-callback.md