웹뷰에서 구글, 페이스북 로그인 구현하기
스윙투앱에서 구글, 페이스북 로그인 보안으로 인한 문제 해결하기
안드로이드 앱 개발 중 구글 및 페이스북과 같은 플랫폼의 로그인 기능을 웹뷰에서 사용하려는 경우, 일반적으로 보안 상의 이유로 인해 구글과 페이스북이 해당 기능을 차단하는 경우가 있습니다.
(아래와 같은 메시지를 발견했다면 해당 이유로 차단된 경우입니다. )
-Google Login 웹뷰에서 차단

-Facebook Login 웹뷰에서 차단

이러한 차단은 앱 사용자의 개인 정보와 계정 보안을 보호하기 위한 조치로 이해해야 합니다.
이 글에서는 User Agent Disallow 이슈에 대해 자세히 알아보고, 이를 회피하기 위한 대안적인 접근 방식을 소개하겠습니다.
User Agent Disallow란 무엇인가요?- User Agent는 클라이언트(웹 브라우저 또는 웹뷰)가 서버에게 자신의 속성을 알리기 위해 전송하는 HTTP 헤더입니다.
- User Agent Disallow는 구글과 페이스북이 자체 로그인 기능을 웹뷰를 통해 사용하는 것을 막기 위해 해당 User Agent 문자열을 감지하여 차단하는 것을 의미합니다.
- User Agent Disallow 이슈의 이유는 무엇인가요?
- 웹뷰는 모바일 앱 안에 내장된 경량화된 웹 브라우저로써, 보안상의 이유로 특정 기능을 차단하는 것이 필요합니다.
- User Agent Disallow는 보안을 강화하기 위한 방식 중 하나로서, 웹뷰에서는 구글 및 페이스북과 같은 로그인 API를 사용하는 것을 제한하여 악용을 방지합니다.
- User Agent Disallow를 회피하는 대안적인 방법은 무엇인가요? 가장 일반적인 방법으로는 다음과 같은 접근 방식을 고려할 수 있습니다:a. 외부 브라우저 열기:
- 사용자가 구글 로그인 또는 페이스북 로그인을 선택하면, 웹뷰에서는 외부 브라우저를 열어 해당 로그인 페이지를 표시합니다.
- 외부 브라우저는 웹뷰와는 별개의 애플리케이션으로 간주되므로, 보안 정책에 영향을 받지 않습니다.
- 로그인이 완료되면, 외부 브라우저는 로그인 결과를 콜백 URL을 통해 전달하고, 애플리케이션이 이를 캡처하여 처리할 수 있습니다.
- 가짜 UserAgent 를 사용하면 구글 로그인의 경우는 해당 이슈를 회시 할 수 있다. 다만 페이스북은 회피가 불가능하다.
📌스윙투앱에서 UserAgent 변경방법
앱 제작 -> 고급 설정 -> 설정 창 콘솔박스 선택: “앱 제작 설정” 탭 선택 ->아래의 두개 값 변경
IOS 웹뷰 UserAgent 값 / 안드로이드 웹뷰 UserAgent 값

(1) Android WebView UserAgent
-> 구글 로그인 회피 가능한 UserAgent 값 (Android)
“Mozilla/5.0 (Linux; Android 9; SM-G950N) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Mobile Safari/537.36”
(2) IOS WebView UserAgent
-> 구글 로그인 회피 가능한 UserAgent 값 (iOS)
“Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1”
수정, 셋팅이 완료되면 앱 업데이트 해주셔야 합니다.
스토어에 출시된 앱은, 새로 제작된 버전으로 앱 업데이트 제출 해주셔야 반영됩니다.
스토어 출시 전 앱은 앱 업데이트 후 새 버전으로 확인해주세요.
📌외부 브라우저 열기 방법으로 구글 및 페이스북 로그인 회피하는 방법 알아보기
#실행 프로세스
(1) 웹뷰에서 login_example.html 을 앱 내 내장 브라우저로 실행한다
login_example.html 실행 예제 코드를 아래에 첨부되었습니다.
login_example.html 실행 파라미터중 customUrl 항목이 있는데 해당 항목은 고급설정에서 설정하실 수 있습니다. 설정된 값을 사용하셔도 됩니다.
*custom url scheme 값은 앱 마다 고유값으로 설정하는 것을 권장드립니다.

앱제작 ->고급설정 ->앱제작설정 탭 선택 -> 앱 URL스키마(custom url scheme) 확인
실행코드복사
swingWebViewPlugin.app.methods.openBrowser('https://www.swing2app.co.kr/sns_login_temp/sns_login_temp_ext.html?customUrl=myapp1706');
- 자세한 브라우저 실행 API 는 아래의 Javascript 연동 API를 참고하세요.
https://documentation.swing2app.co.kr/developer/webview/javascript-api
아래의 코드를 활용하여 로그인 페이지를 만들어주세요.
- 아래의 코드에서 todo 에 기재된 facebook 앱 아이디와 구글 client id 는 반드시 변경후에 사용해야 합니다.
<html>
<head>
<!-- Title -->
<title>Swing-test</title>
<meta content="width=device-width, initial-scale=1" name="viewport">
<meta charset="UTF-8">
<!-- Javascripts -->
<script src="/assets/plugins/jquery/jquery-2.1.4.min.js"></script>
<script>
var host = location.host.toLowerCase();
const urlParams = new URLSearchParams(window.location.search);
var customUrl = urlParams.get('customUrl');
var autoType = urlParams.get('autoType');
$(document).ready(function () {
window.fbAsyncInit = function() {
FB.init({
appId : '464485827223968', // todo facebook 앱 아이디를 입력
xfbml : true,
version : 'v9.0'
});
FB.AppEvents.logPageView();
if( autoType == "facebook"){
fb_login();
}
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
});
var googleAuth2 = null;
function fb_login(){
$("#infoBox").append("fb_login start");
FB.login(function(response) {
if (response.authResponse) {
console.log('Welcome! Fetching your information.... ');
var access_token = response.authResponse.accessToken; //get access token
if (response.status === 'connected') { // Logged into your webpage and Facebook.
gotoSnsLoginPageFn("facebook", access_token);
} else {
}
} else {
//user hit cancel button
console.log('User cancelled login or did not fully authorize.');
}
}, {
scope: 'public_profile,email'
});
}
function googleInitFn() {
gapi.load('auth2', function() {
/* Ready. Make a call to gapi.auth2.init or some other API */
googleAuth2 = gapi.auth2.init({
client_id: '829822048277-oncammdmeq2mku9lbqndknoustamqcbq.apps.googleusercontent.com', // todo google client_id 입력
cookiepolicy: 'single_host_origin',
scope: 'profile email'
});
attachSignin(document.getElementById('googleLoginBtn'));
if( autoType == "google" ) {
setTimeout(function() {
$("#googleLoginBtn")[0].click();
},1000);
}
});
}
function attachSignin(element) {
console.log(element.id);
googleAuth2.attachClickHandler(element, {},
function(googleUser) {
var loginAuth2 = gapi.auth2.getAuthInstance();
if (loginAuth2.isSignedIn.get()) {
var authToken = loginAuth2.currentUser.get().getAuthResponse().id_token;
gotoSnsLoginPageFn("google", authToken);
}
}, function(error) {
// alert(JSON.stringify(error, undefined, 2));
});
}
function googleLoginActionFn(){
googleAuth2.signIn().then(function(user){
if (user.isSignedIn()) {
var authToken = user.getAuthResponse().id_token;
gotoSnsLoginPageFn("google", authToken);
}
});
}
function gotoSnsLoginPageFn(idType, token){
var $form = $("#snsLoginForm");
$form.find("[name=id_type]").val(idType);
$form.find("[name=token]").val(token);
var runUrl = customUrl +'://app?function=command&linkUrl=' + btoa( "loginTokenCallback('"+ token +"','" + idType + "');" );
$('.return-app-area a').attr('href', runUrl );
$('.sns-login-area').hide();
$('.return-app-area').show();
if( location.href.indexOf('swing2app.com') >= 0 )
{
$('.command-button').text('Login successful - Proceed');
}
else {
$('.command-button').text('로그인 성공 - 이동하기');
}
}
</script>
<meta name="google-signin-client_id" content="829822048277-oncammdmeq2mku9lbqndknoustamqcbq.apps.googleusercontent.com"> <!-- todo google signin-client-id 입력 -->
<script src="https://apis.google.com/js/platform.js?onload=googleInitFn" async defer></script>
<script>
</script>
</head>
<body>
<div class="sns-login-area" style="width: 100%; padding:30px">
<div class="sns-login-btn-box facebook">
<div class="sns-login-main-btn" id="snsFaceBookLoginBtn" onclick="fb_login();">
<span class="icon"><img src="/assets/images/sns_login/facebook.png" /></span>
<span class="buttonText">페이스북으로 로그인</span>
</div>
</div>
<div class="sns-login-btn-box google">
<div id="googleLoginBtn" class="customGPlusSignIn sns-login-main-btn">
<span class="icon"><img src="/assets/images/sns_login/google.png" /></span>
<span class="buttonText">구글로 로그인</span>
</div>
</div>
</div>
<div class="return-app-area" style="display: none;padding: 30px;text-align: center;background: #e6e6e6;"><a href="#" class="command-button" style="color: black;text-decoration: none;font-size: 25px;">로그인 성공 - 이동하기</a>
</div>
<div id="infoBox" style="display: none">
</div>
</body>
</html>
(2) 웹사이트에서 callback 함수를 정의하여 device token 을 받기
1번 브라우저에서 로그인이 완료되면 아래의 정의된 callback 함수로 deviceToken 이 전달된다, idType 값은 login_example.html 정의된 값으로 전달되는데 제공되는 소스에서는 “facebook”, “google”로 구분되어 들어오게 되어있다.
사이트내 callback 구현복사
function loginTokenCallback(token, idType) {
console.log("token : " + token );
console.log("idType : " + idType );
}
(3) 전달받은 accessToken 으로 로그인 구현하기
이후에는 전달받은 accessToken 으로 웹사이트내에서 사용자 정보를 가져와 연동 로그인을 구현해주면 된다.


