2020.12.06 更新:本站於今日開始進行改版,本篇原本要放登入按鈕的地方,尚未規劃出移到哪個區塊,因此本站用 Auth0 的功能先暫停,直到找到適合的擺放位置。
自己的會員功能自己寫
在上一篇〈Auth0 Universal Login,做一個自己的會員註冊、登入功能〉中,寫了一個靜態頁面來接 Auth0 的 Universal Login。後來想想,如果要使用者從 Let’s Write 本站先到靜態頁,再從靜態頁進 Universal Login 的頁面,中間多了一步,容易造成使用者的疑惑跟跳出率增加。另外,登入後的個人資訊也無法呈現在本站上,因此決定花一點時間,把 Auth0 的會員註冊、登入功能直接搬到本站來。
Auth0 在 WordPress 上有外掛,有試用了一下,但不知道為何小工具那塊一直是空白,而且它的設定值,登入後就一定要轉到指定的頁面,無法回到點擊「登入」時的原頁,以上種種,再加上身為前端的驕傲 XD,August 決定自己寫一個。
因為本站是用 WordPress 架設的,PHP 的部份 August 並不會,所以本篇是直接用 JavaScript 來接 Auth0 的 API。
接的過程中踩了點坑,會把踩坑跟跳出坑的部份也寫在這篇筆記文裡。
寫 code 前的必要步驟,跳過
在開始寫 code 前有些必要步驟,像是註冊 Auth0、建立 Auth0 App、取得 Client ID、Domain……等,本篇跳過,因為都寫在上一篇中了,這邊就不再重覆寫。
Auth0 Universal Login,做一個自己的會員註冊、登入功能
另外,大部份用 Auth0 接會員登入的 code,本篇也是直接拿上一篇的原始碼來改,可以直接從 GitHub 上下載上一篇完整的原始碼。
https://github.com/letswritetw/letswrite-auth0-login
1 頁面新增登入按鈕
插入登入按鈕的方式,如果對 PHP 很熟,或是 WordPress 架構很熟的朋友,應該會直接改原有的程式碼。
但 August 對這二個不太熟 XD~而且以前遇過,從佈景主題編輯器上修改 code 後,一遇到佈景主題有更新,原本加進去的 code 就不見了的狀況。
所以在新增一個登入按鈕的部份,各頁右上角的小按鈕是用 JavaScript 來新增,而各頁右側因為本來小工具就可以加區塊了,就用小工具貼上 HTML 來新增。
這段寫的是用 JS 新增的 code。
確定好按鈕的 HTML,把它寫進變數裡:
const loginHTML = '<div id="letswrite-login"><button class="auth0_login btn btn-letswrite small" id="auth0_login" type="button"><i class="fa fa-user"></i><span>會員 註冊 / 登入</span></button><div class="letswrite-user d-none"><button class="auth0_logout btn btn-letswrite small" id="auth0_logout" type="button">登出</button></div></div>';
CSS 的部份就寫進 WordPress 後台的「編輯 CSS」中。
有了 HTML,接著就是要把按鈕 insert 進指定的區塊中,這邊用的是 insertAdjacentHTML
,這是個好物,自從知道有這 function 後,就不太用 appendChild
了。
insertAdjacentHTML
的用法可以看 MDN 上的說明:Element.insertAdjacentHTML()
完整加上按鈕的 code:
const loginHTMLWrap = document.querySelector('.header-links'); const loginHTML = '<div id="letswrite-login"><button class="auth0_login btn btn-letswrite small" id="auth0_login" type="button"><i class="fa fa-user"></i><span>會員 註冊 / 登入</span></button><div class="letswrite-user d-none"><button class="auth0_logout btn btn-letswrite small" id="auth0_logout" type="button">登出</button></div></div>'; loginHTMLWrap.insertAdjacentHTML('beforeend', loginHTML);
2 登入按鈕的點擊事件
頁面寫進登入按鈕後,下一步就是要寫按鈕的 onclick
。
onclick 時執行 login(),這是 Auth0 的基本用法,上一篇中也有寫到,這邊要額外處理的部份,是一個坑。
在 Auth0 的後台 App 設定中,白名單的部份是不能寫規則運算式的,也就是說不能寫成這樣:
https://letswrite.tw/*
不能用一個 * 符號代表 letswrite.tw 底下的所有頁面,它必須寫一個絕對路徑,否則就一律阻擋。
但,總不能把每一篇文章的網址都手動輸入進後台吧?這有點石器時代的作法。
也不能要求使用者一定只能在首頁做登入吧?這樣子的 UX 有點廢。
Auth0 的說明文件中,是有提供 Pop Up 一個視窗出來,在那個視窗做登入的作法,不過,不能確定 Pop Up 在手機上的應用程度,會不會出現什麼樣子神奇的 bug?(對,就是在說 iOS Safari,根本手機界的 IE)
後來想到的作法,步驟如下:
- 先把點擊按鈕的當下,該頁的網址存進 cookies 中。
- 如果當下的頁面不在首頁,就把頁面轉到首頁,首頁判斷 cookies 中有沒有值,有的話就執行 login 的 function。
- 使用者登入回到首頁後,首頁一樣判斷 cookies 的值存不存在,存在就轉回到第一步中的頁面,並把 cookies 給清掉。
這三步看完,乍看之下沒有問題,但實際上存在著另一個問題,就是如果使用者點了登入按鈕,進到 Auth0 的 Universal Login 的頁面後,沒有想要註冊或登入會員,又按了上一步回到上一頁呢?那因為 cookies 一直有值,頁面就會一直做轉址的動作。
所以在實際寫 code 時,修改後的步驟如下:
- 先把點擊按鈕的當下,該頁的網址存進 cookies 中。
- 如果當下的頁面不在首頁,就把頁面轉到首頁。首頁判斷 cookies 中有沒有值,有的話就把 cookies 中存進的網址,再存進第二組c ookies中,並執行 login 的 function。
- 使用者登入回到首頁後,首頁判斷第二組 cookies 的值存不存在,存在就轉址,並把二組 cookies 都給清掉。
登出的部份情況一樣,Auth0 也是有限登出後要轉址到的頁面,設白名單也一樣無法用規則運算式,所以也是要把使用者按下「登出」時,當下頁面的網址給存到 cookies 中,再轉址回首頁做登出,登出完回來後,cookies 中有值就執行轉址回到原頁,並把 cookies 給刪掉。
存、取、刪 cookies 的部份,因為懶,直接用了一個套件:JavaScript Cookie。
按下登入、登出後的程式碼:
3 使用者登入後,抓使用者資訊並呈現在頁面上
Auth0 登入後,可以直接用 auth0.getUser()
來取得登入者的資訊。
這邊也踩到了一個坑,從上一篇開始,用 Auth0 Universal Login 的方式,都是引用它們的 auth0-spa-js
。
用這個 SDK 來登入會有一個問題,因為這是給 SPA(Single-page application)這種一頁式網站來使用,而 WordPress 並不是單頁的,因此在執行 auth0.isAuthenticated()
來判斷使用者是否登入時,除了使用者有確實進到 Universal Login 的頁面做登入的那一次會回傳 true,其它時候都會回傳 false,並且無法透過 auth0.getUser()
來取得使用者資訊。
而且當使用者在 WordPress 上點選任何一篇文章,整個頁面會重新載入,就代表使用者只要進到別篇文章, auth0.isAuthenticated()
永遠都會回傳 false,然後就抓不到使用者登入的資訊。
後來再查了一下官方的說明文件〈Auth0 Single Page App SDK〉,裡面有提供了另一種取得使用者登入資訊的方試,就是拿 access_token 去取。
使用者從 Universal Login 頁面登入回來後,執行 auth0.getTokenSilently()
就可以取得 access_token。那我們要做的就是把這組 token 存下來,當使用者進到別頁後,用這組 token 去取得資訊回來就行了。
那當然,前端能想到的,其實也就是存進 cookies 裡。是說把東西存到 cookies 總覺得有點不安全,但 Auth0 產生的 access_token 會是一組亂數,而且可以自己在後台設定 token 的有效期間,基本上要猜出別的使用者的 token 很難,如果遇到可以破解 token 的高手高手高高手,那……即便不是存在 cookies 裡大概也擋不住了。
這邊 August 提供用 access_token 取得使用者資訊的 code:
fetch 的 URL 記得替換成 Auth0 APP 中提供的 Domain。
放置完成的 JavaScript 檔
JS 檔完成後,最後就是要放進頁面裡。
原本一開始 August 是直接把 script src 寫在小工具的自訂 HTML 裡,後來發現應該是 WordPress 吐出來的小工具位置不對,一直無法執行 JS 檔裡的 function,最後決定放在 GTM中,才成功執行。

