Firebase - 암호 재설정 랜딩 페이지 사용자 정의
파이어베이스에서 비밀번호 재설정 랜딩 페이지를 사용자 정의할 수 있습니까?제 앱이 영어가 아니기 때문에 저는 그 페이지를 현지화하고 싶습니다.그렇게 할 수 있는 방법이 있습니까?
잘 부탁드립니다.
재설정 은 아래암전메사지수있습다니에서 지정할 수 .Firebase Console -> Auth -> Email Templates -> Password Reset
이메일의 링크를 변경하여 자신의 페이지를 가리킵니다.로 고는 다음과 .<code>
자리 표시자가 URL의 암호 재설정 코드로 대체됩니다.
그런 다음 사용자 정의 페이지에서 URL에서 암호 재설정 코드를 읽고 다음을 수행할 수 있습니다.
firebase.auth().confirmPasswordReset(code, newPassword)
.then(function() {
// Success
})
.catch(function() {
// Invalid code
})
선택적으로 암호 재설정 양식을 표시하기 전에 먼저 코드가 유효한지 확인할 수 있습니다.
firebase.auth().verifyPasswordResetCode(code)
.then(function(email) {
// Display a "new password" form with the user's email address
})
.catch(function() {
// Invalid code
})
https://firebase.google.com/docs/reference/js/firebase.auth.Auth#confirmPasswordReset https://firebase.google.com/docs/reference/js/firebase.auth.Auth#verifyPasswordResetCode
2022년 12월 답변
를 바꿉니다.const firebaseConfig = {};
firebaseConfig와 함께 아래 코드로 작업 중인 사용자 지정 전자 메일 인증 처리기 페이지를 가지고 있습니다.
파이어베이스 인증 작업에 사용자 지정 전자 메일 핸들러 페이지를 사용하려는 이유는 많습니다.
- Firebase 비브랜드 도메인이 아닌 사용자 정의 도메인을 사용하면 사용자 정의 전자 메일 핸들러 페이지에 사용자 고유의 스타일과 로고 등이 있을 수 있습니다.는 서브도메인 하위도으로갔다습에 .
https://auth.mydomain.com
그리고 아래 코드는 루트에 있는 index.dll에 있습니다. 메일 핸들러 되고 전자 는 " " " " Firebase " "와 같이 .https://auth.mydomain.com/?mode=resetPassword&oobCode=longStringCode&apiKey=apiCodeString&lang=en
- 아래의 상용판 코드를 사용하여 파이어베이스 호스팅(여기서는 다루지 않음)에서 호스팅하는 것은 매우 쉽습니다.
- Firebase 기본 재설정 비밀번호 페이지 핸들러는 >= 6자의 비밀번호 요구사항만 설정합니다.아직 암호 복잡성을 구성할 수 없습니다.이러한 이유만으로 사용자 지정 전자 메일 작업 처리기 페이지를 만드는 것으로 충분했습니다.
참고: 사용자 지정 작업 처리기 URL을 설정할 때 URL이 가리키는 페이지는 모든 전자 메일 작업 모드를 처리해야 합니다.예를 들어, 암호 재설정에 대한 사용자 지정 URL을 설정하고 템플릿 콘솔에서 전자 메일 확인 URL의 기본값을 사용할 수는 없습니다.사용자 지정 전자 메일 핸들러 페이지 URL에서 모든 모드를 처리해야 합니다.아래 코드는 모든 모드를 처리합니다.
코드에서 다음을 찾을 수 있습니다.validatePasswordComplexity
볼 수 비밀번호 .아래 스크린샷에서 볼 수 있듯이 현재 최소 암호 복잡도 요구 사항을 표시하도록 설정되어 있습니다.문자를 할 에 대한 사항을 예를 들어 사용자가 특수 문자를 입력하면 암호가 복잡성 요구 사항을 충족하고 경고가 사라질 때까지 특수 문자 누락에 대한 빨간색 경고가 사라집니다.복잡성 요구 사항이 충족될 때까지 암호를 재설정할 수 없습니다.가 두를 들어, " " " 를 합니다." (" " " " " )hasMinSpecialChar
정규식 변경{1,}
로.{2,}
.
사용자 지정 인증 전자 메일 모드 처리기
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Firebase Auth Handlers</title>
<meta name="robots" content="noindex">
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css" />
<style>
body {
font-family: Roboto,sans-serif;
}
i {
margin-left: -30px;
cursor: pointer;
}
.button {
background-color: #141645;
border: none;
color: white;
padding: 11px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 4px;
}
input {
width: 200px;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.red-alert {
color: #B71C1C;
}
.center {
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
text-align: center;
}
#cover-spin {
position:fixed;
width:100%;
left:0;right:0;top:0;bottom:0;
background-color: rgba(255,255,255,0.7);
z-index:9999;
}
@-webkit-keyframes spin {
from {-webkit-transform:rotate(0deg);}
to {-webkit-transform:rotate(360deg);}
}
@keyframes spin {
from {transform:rotate(0deg);}
to {transform:rotate(360deg);}
}
#cover-spin::after {
content:'';
display:block;
position:absolute;
left:48%;top:40%;
width:40px;height:40px;
border-style:solid;
border-color:black;
border-top-color:transparent;
border-width: 4px;
border-radius:50%;
-webkit-animation: spin .8s linear infinite;
animation: spin .8s linear infinite;
}
</style>
<script>
const AuthHandler = {
init: props => {
AuthHandler.conf = props
AuthHandler.bindMode()
},
bindMode: () => {
switch (AuthHandler.conf.mode) {
case 'resetPassword':
AuthHandler.setModeTitle('Password Reset')
if (!AuthHandler.validateRequiredAuthParams()) {
AuthHandler.displayErrorMessage(AuthHandler.conf.defaultErrorMessage)
return
}
AuthHandler.handleResetPassword()
break;
case 'recoverEmail':
AuthHandler.setModeTitle('Email Recovery')
if (!AuthHandler.validateRequiredAuthParams()) {
AuthHandler.displayErrorMessage(AuthHandler.conf.defaultErrorMessage)
return
}
AuthHandler.handleRecoverEmail()
break;
case 'verifyEmail':
AuthHandler.setModeTitle('Email Verification')
if (!AuthHandler.validateRequiredAuthParams()) {
AuthHandler.displayErrorMessage(AuthHandler.conf.defaultErrorMessage)
return
}
AuthHandler.handleVerifyEmail()
break;
default:
AuthHandler.displayErrorMessage(AuthHandler.conf.defaultErrorMessage)
break;
}
},
handleResetPassword: () => {
AuthHandler.showLoadingSpinner()
// Verify the code is valid before displaying the reset password form.
AuthHandler.conf.verifyPasswordResetCode(AuthHandler.conf.auth, AuthHandler.conf.oobCode).then(() => {
AuthHandler.hideLoadingSpinner()
// Display the form if we have a valid reset code.
AuthHandler.showPasswordResetForm()
AuthHandler.conf.passwordField.addEventListener('input', AuthHandler.validatePasswordComplexity);
AuthHandler.conf.passwordToggleButton.addEventListener('click', e => {
AuthHandler.conf.passwordField.setAttribute(
'type',
AuthHandler.conf.passwordField.getAttribute('type') === 'password'
? 'text' : 'password');
e.target.classList.toggle('bi-eye');
});
AuthHandler.conf.passwordResetButton.addEventListener('click', () => {
AuthHandler.hideMessages()
// Test the password again. If it does not pass, errors will display.
if (AuthHandler.validatePasswordComplexity(AuthHandler.conf.passwordField)) {
AuthHandler.showLoadingSpinner()
// Attempt to reset the password.
AuthHandler.conf.confirmPasswordReset(
AuthHandler.conf.auth,
AuthHandler.conf.oobCode,
AuthHandler.conf.passwordField.value.trim()
).then(() => {
AuthHandler.hidePasswordResetForm()
AuthHandler.hideLoadingSpinner()
AuthHandler.displaySuccessMessage('Password has been reset!')
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displayErrorMessage('Password reset failed. Please try again.')
})
}
});
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.hidePasswordResetForm()
AuthHandler.displayErrorMessage('Invalid password reset code. Please try again.')
});
},
handleRecoverEmail: () => {
AuthHandler.showLoadingSpinner()
let restoredEmail = null;
AuthHandler.conf.checkActionCode(AuthHandler.conf.auth, AuthHandler.conf.oobCode).then(info => {
restoredEmail = info['data']['email'];
AuthHandler.conf.applyActionCode(AuthHandler.conf.auth, AuthHandler.conf.oobCode).then(() => {
AuthHandler.conf.sendPasswordResetEmail(AuthHandler.conf.auth, restoredEmail).then(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displaySuccessMessage(`Your email has been restored and a reset password email has been sent to ${restoredEmail}. For security, please reset your password immediately.`)
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displaySuccessMessage(`Your email ${restoredEmail} has been restored. For security, please reset your password immediately.`)
})
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displayErrorMessage('Sorry, something went wrong recovering your email. Please try again or contact support.')
})
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displayErrorMessage('Invalid action code. Please try again.')
})
},
handleVerifyEmail: () => {
AuthHandler.showLoadingSpinner()
AuthHandler.conf.applyActionCode(AuthHandler.conf.auth, AuthHandler.conf.oobCode).then(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displaySuccessMessage('Email verified! Your account is now active. Time to send some messages!')
}).catch(() => {
AuthHandler.hideLoadingSpinner()
AuthHandler.displayErrorMessage('Your code is invalid or has expired. Please try to verify your email address again by tapping the resend email button in app.')
})
},
validateRequiredAuthParams: () => {
// Mode is evaluated in the bindMode switch. If no mode will display default error message. So, we're just
// checking for a valid oobCode here.
return !!AuthHandler.conf.oobCode
},
setModeTitle: title => {
AuthHandler.conf.modeTitle.innerText = title
},
validatePasswordComplexity: e => {
const password = !!e.target ? e.target.value.trim() : e.value.trim()
const isValidString = typeof password === 'string'
/// Checks if password has minLength
const hasMinLength = isValidString && password.length >= 8
AuthHandler.conf.passwordHasMinLength.style.display = hasMinLength ? 'none' : ''
/// Checks if password has at least 1 normal char letter matches
const hasMinNormalChar = isValidString && password.toUpperCase().match(RegExp('^(.*?[A-Z]){1,}')) !== null
AuthHandler.conf.passwordHasMinNormalChar.style.display = hasMinNormalChar ? 'none' : ''
/// Checks if password has at least 1 uppercase letter matches
const hasMinUppercase =
isValidString && password.match(RegExp('^(.*?[A-Z]){1,}')) !== null
AuthHandler.conf.passwordHasMinUppercase.style.display = hasMinUppercase ? 'none' : ''
/// Checks if password has at least 1 numeric character matches
const hasMinNumericChar =
isValidString && password.match(RegExp('^(.*?[0-9]){1,}')) !== null
AuthHandler.conf.passwordHasMinNumericChar.style.display = hasMinNumericChar ? 'none' : ''
/// Checks if password has at least 1 special character matches
const hasMinSpecialChar = isValidString && password.match(RegExp("^(.*?[\$&+,:;/=?@#|'<>.^*()_%!-]){1,}")) !== null
AuthHandler.conf.passwordHasMinSpecialChar.style.display = hasMinSpecialChar ? 'none' : ''
const passing = hasMinLength &&
hasMinNormalChar &&
hasMinUppercase &&
hasMinNumericChar &&
hasMinSpecialChar
AuthHandler.conf.passwordIncreaseComplexity.style.display = passing ? 'none' : ''
return passing
},
showLoadingSpinner: () => {
AuthHandler.conf.loading.style.display = ''
},
hideLoadingSpinner: () => {
AuthHandler.conf.loading.style.display = 'none'
},
showPasswordResetForm: () => {
AuthHandler.conf.passwordForm.style.display = '';
},
hidePasswordResetForm: () => {
AuthHandler.conf.passwordForm.style.display = 'none';
},
displaySuccessMessage: message => {
AuthHandler.hideErrorMessage()
AuthHandler.conf.success.innerText = message
AuthHandler.conf.success.style.display = ''
},
hideSuccessMessage: () => {
AuthHandler.conf.success.innerText = ''
AuthHandler.conf.success.style.display = 'none'
},
displayErrorMessage: message => {
AuthHandler.hideSuccessMessage()
AuthHandler.conf.error.innerText = message
AuthHandler.conf.error.style.display = ''
},
hideErrorMessage: () => {
AuthHandler.conf.error.innerText = ''
AuthHandler.conf.error.style.display = 'none'
},
hideMessages: () => {
AuthHandler.hideErrorMessage()
AuthHandler.hideSuccessMessage()
},
}
</script>
</head>
<body>
<div class="center">
<div id="cover-spin" style="display: none;"></div>
<p>
<image src="https://via.placeholder.com/400x70/000000/FFFFFF?text=Your+Logo"/>
</p>
<p id="mode-title" style="font-size: 20px; font-weight: bold;"></p>
<p id="error" class="red-alert" style="display: none;"></p>
<p id="success" style="display: none;"></p>
<div id="password-form" style="min-width: 700px; min-height: 300px; display: none;">
<label for="password">New Password</label>
<input id="password" type="password" minlength="8" maxlength="35" autocomplete="off" placeholder="Enter new password" style="margin-left: 10px;" required>
<i class="bi bi-eye-slash" id="toggle-password"></i>
<button id="reset-button" type="button" class="button" style="margin-left: 20px;">Reset</button>
<p class="red-alert" id="increase-complexity" style="display: none;"><strong>Increase Complexity</strong></p>
<p class="red-alert" id="has-min-length" style="display: none;">Minimum 8 characters</p>
<p class="red-alert" id="has-min-normal-char" style="display: none;">Minimum 1 normal characters</p>
<p class="red-alert" id="has-min-uppercase" style="display: none;">Minimum 1 uppercase characters</p>
<p class="red-alert" id="has-min-numeric-char" style="display: none;">Minimum 1 numeric characters</p>
<p class="red-alert" id="has-min-special-char" style="display: none;">Minimum 1 special characters</p>
</div>
</div>
<script type="module">
// https://firebase.google.com/docs/web/setup#available-libraries
// https://firebase.google.com/docs/web/alt-setup
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js';
import {
applyActionCode,
checkActionCode,
confirmPasswordReset,
getAuth,
sendPasswordResetEmail,
verifyPasswordResetCode,
} from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js';
// Replace {} with your firebaseConfig
// https://firebase.google.com/docs/web/learn-more#config-object
const firebaseConfig = {};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
document.addEventListener('DOMContentLoaded', () => {
// Get the mode and oobCode from url params
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
AuthHandler.init({
app,
auth,
applyActionCode,
checkActionCode,
confirmPasswordReset,
getAuth,
sendPasswordResetEmail,
verifyPasswordResetCode,
// Used by all modes to display error or success messages
error: document.getElementById('error'),
success: document.getElementById('success'),
// https://firebase.google.com/docs/auth/custom-email-handler#create_the_email_action_handler_page
mode: params.mode,
oobCode: params.oobCode,
modeTitle: document.getElementById('mode-title'),
loading: document.getElementById('cover-spin'),
// Password reset elements
passwordForm: document.getElementById('password-form'),
passwordField: document.getElementById('password'),
passwordResetButton: document.getElementById('reset-button'),
passwordToggleButton: document.getElementById('toggle-password'),
passwordHasMinLength: document.getElementById('has-min-length'),
passwordHasMinNormalChar: document.getElementById('has-min-normal-char'),
passwordHasMinUppercase: document.getElementById('has-min-uppercase'),
passwordHasMinNumericChar: document.getElementById('has-min-numeric-char'),
passwordHasMinSpecialChar: document.getElementById('has-min-special-char'),
passwordIncreaseComplexity: document.getElementById('increase-complexity'),
defaultErrorMessage: 'Invalid auth parameters. Please try again.',
});
});
</script>
</body>
</html>
참고: 저는 웹팩을 설정하고 싶지 않았기 때문에 새로운 모듈식 트리 흔들기 방식을 사용하지 않기로 결정했고, 최신 Firebasejs 버전을 사용할 수 있도록 alt 설정 방식을 선택했습니다. 이 문서에서는 v9.15.0입니다.단일 페이지의 사용자 지정 전자 메일 핸들러 페이지에 번짐이 걱정된다면 트리 쉐이킹과 웹 팩을 살펴보십시오.저는 더 적은 구성으로 초고속 옵션을 선택했습니다.
참고: 저는 다음을 처리하지 않습니다.lang
또는apiKey
param이 firebase의 url에 포함되어 있습니다.내 사용 사례는 언어 변경을 수행할 필요가 없습니다.
재설정을 누른 후, Firebase 인증에서 모든 것이 잘되면 사용자는 이것을 볼 수 있습니다.각 작업 모드에 대해 성공 및 오류 메시지가 표시됩니다.
@Channing Huang이 제공한 답은 정답이지만 이 오류가 항상 다음과 같은 것은 아니라는 것을 명심해야 합니다.invalid-code
.
사용자가 나중에 URL을 열지 않거나 다른 경우에 URL을 열지 않은 경우 오류가 만료되었는지 확인하는 중입니다.그런 다음 만료된 경우 다른 전자 메일을 보낼 수 있습니다.
언급URL : https://stackoverflow.com/questions/37932983/firebase-customize-reset-password-landing-page
'programing' 카테고리의 다른 글
최소 두 개의 날짜를 선택합니다. (0) | 2023.06.18 |
---|---|
다중 처리 - 파이프 대 대기열 (0) | 2023.06.18 |
argv[]를 int로 받으려면 어떻게 해야 합니까? (0) | 2023.06.18 |
스케줄링된 작업 만들기 (0) | 2023.06.18 |
새 줄 서식을 Mac에서 Windows로 변환 (0) | 2023.06.18 |