Power Up Gatsby with Auth!

Power Up Gatsby with Auth!

Gatsby rose to fame as a static site generator, but it’s also an amazing platform for building dynamic sites! Wouldn’t it be awesome if we could add a user profile with access to protected data in our Gatsby app? Like Mario’s fire flower or Link’s hook shot, adding auth will power up your dynamic Gatsby site. Just one question: is it any different than adding auth to a regular React app?

In this talk, you’ll learn about Gatsby’s build process and runtime and how they impact setting up auth. You’ll also learn how to use Hooks and Context to build your auth sidekick. Finally, you’ll learn some tips and tricks to avoid common pitfalls with adding auth to Gatsby. By the end, you’ll be ready for the next level of your Gatsby quest!

7beed3a6fa39e12c9e873b903e4d9244?s=128

Sam Julien

May 02, 2020
Tweet

Transcript

  1. None
  2. Power Up Gatsby

  3. Power Up Gatsby with Auth!

  4. @samjulien

  5. Static Site Generator @samjulien

  6. Dynamic Site Framework @samjulien

  7. Blazing Fast Web & App Framework @samjulien

  8. @samjulien

  9. @samjulien +

  10. @samjulien +

  11. @samjulien

  12. Adding Auth to Gatsby @samjulien

  13. Adding Auth to Gatsby @samjulien Build Time vs. Runtime

  14. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth
  15. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth Special Gatsby Bits
  16. @samjulien Sam Julien @samjulien

  17. @samjulien Sam Julien @samjulien Sr. Developer Advocate Engineer at Auth0

  18. @samjulien Sam Julien @samjulien Sr. Developer Advocate Engineer at Auth0

    Google Developer Expert
  19. @samjulien Sam Julien @samjulien Sr. Developer Advocate Engineer at Auth0

    Google Developer Expert Instructor for Thinkster & egghead
  20. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth Special Gatsby Bits
  21. @samjulien

  22. Build Time Runtime

  23. Build Time Runtime

  24. Build Time Runtime

  25. Build Time Runtime

  26. No browser APIs available! @samjulien

  27. Build Time Runtime

  28. Build Time Runtime

  29. Build Time Runtime

  30. Build Time Runtime

  31. Build Time Runtime

  32. Build Time Runtime

  33. Build Time Runtime

  34. @samjulien +

  35. @samjulien +

  36. @samjulien

  37. Start login Handle redirect Log in to provider @samjulien

  38. Handle redirect Success Error @samjulien

  39. Success User Token @samjulien

  40. Success User Token Redirect @samjulien

  41. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth Special Gatsby Bits
  42. @samjulien

  43. Auth Utility @samjulien

  44. @samjulien Auth Utility

  45. @samjulien Store token and user in memory Auth Utility

  46. @samjulien Store token and user in memory Interact with auth

    SDK Auth Utility
  47. @samjulien Store token and user in memory Interact with auth

    SDK Handle all redirect logic Auth Utility
  48. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  49. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  50. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  51. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  52. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  53. @samjulien export const AuthProvider = ({ children, ...initOptions }) =>

    {...};
  54. @samjulien export const AuthProvider = ({ children, ...initOptions }) =>

    { };
  55. @samjulien export const AuthProvider = ({ children, ...initOptions }) =>

    { ...};
  56. @samjulien ...

  57. @samjulien const [authClient, setAuthClient] = useState(); const [isAuthenticated, setIsAuthenticated] =

    useState(); const [token, setToken] = useState(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true);
  58. @samjulien const [authClient, setAuthClient] = useState(); const [isAuthenticated, setIsAuthenticated] =

    useState(); const [token, setToken] = useState(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true);
  59. @samjulien const [authClient, setAuthClient] = useState(); const [isAuthenticated, setIsAuthenticated] =

    useState(); const [token, setToken] = useState(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true);
  60. @samjulien const [authClient, setAuthClient] = useState(); const [isAuthenticated, setIsAuthenticated] =

    useState(); const [token, setToken] = useState(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true);
  61. @samjulien const [authClient, setAuthClient] = useState(); const [isAuthenticated, setIsAuthenticated] =

    useState(); const [token, setToken] = useState(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true);
  62. @samjulien useEffect(() => { const initAuth = async () =>

    { // initialize the auth client const authFromHook = await createAuthClient(initOptions); setAuth(authFromHook); // check if we're in the middle of a redirect if ( window.location.search.includes("code=") && window.location.search.includes("state=") ) { const { appState } = await authFromHook.handleRedirectCallback(); onRedirectCallback(appState); }
  63. @samjulien useEffect(() => { const initAuth = async () =>

    { // initialize the auth client const authFromHook = await createAuthClient(initOptions); setAuth(authFromHook); // check if we're in the middle of a redirect if ( window.location.search.includes("code=") && window.location.search.includes("state=") ) { const { appState } = await authFromHook.handleRedirectCallback(); onRedirectCallback(appState); }
  64. @samjulien useEffect(() => { const initAuth = async () =>

    { // initialize the auth client const authFromHook = await createAuthClient(initOptions); setAuth(authFromHook); // check if we're in the middle of a redirect if ( window.location.search.includes("code=") && window.location.search.includes("state=") ) { const { appState } = await authFromHook.handleRedirectCallback(); onRedirectCallback(appState); }
  65. @samjulien useEffect(() => { const initAuth = async () =>

    { // initialize the auth client const authFromHook = await createAuthClient(initOptions); setAuth(authFromHook); // check if we're in the middle of a redirect if ( window.location.search.includes("code=") && window.location.search.includes("state=") ) { const { appState } = await authFromHook.handleRedirectCallback(); onRedirectCallback(appState); }
  66. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  67. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  68. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  69. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  70. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  71. @samjulien // set initial state using hooks const isAuthenticated =

    await authFromHook.isAuthenticated(); setIsAuthenticated(isAuthenticated); if (isAuthenticated) { const user = await authFromHook.getUser(); setUser(user); } setLoading(false); }; initAuth(); }, []);
  72. @samjulien const handleRedirectCallback = async () => { // handle

    callback // use hooks to set state };
  73. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  74. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  75. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  76. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  77. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  78. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  79. Auth Utility @samjulien

  80. @samjulien Auth Utility

  81. @samjulien Store token and user in memory Interact with auth

    SDK Handle all redirect logic Auth Utility
  82. @samjulien

  83. Where do we use this AuthProvider? @samjulien

  84. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth Special Gatsby Bits
  85. Where do we use this AuthProvider? @samjulien

  86. gatsby-browser.js @samjulien

  87. export const wrapRootElement = ({ element }) => { return

    ( <AuthProvider // pass in any configuration here > {element} </AuthProvider> ); }; @samjulien
  88. export const wrapRootElement = ({ element }) => { return

    ( <AuthProvider // pass in any configuration here > {element} </AuthProvider> ); }; @samjulien
  89. export const wrapRootElement = ({ element }) => { return

    ( <AuthProvider // pass in any configuration here > {element} </AuthProvider> ); }; @samjulien
  90. Does our app work? @samjulien

  91. Maybe. @samjulien

  92. gatsby develop vs gatsby build @samjulien

  93. None
  94. None
  95. Build Time Runtime

  96. Build Time Runtime

  97. Build Time Runtime

  98. Build Time Runtime

  99. Build Time Runtime

  100. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  101. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  102. @samjulien const defaultContext = { isAuthenticated: false, user: null, token:

    null, loading: false, login: () => {}, };
  103. Why do we do this? @samjulien

  104. Example: NavBar Component @samjulien

  105. @samjulien const NavBar = () => { const { isAuthenticated,

    login, logout } = useAuth(); return ( <div> <Link to="/">Home</Link>{" "} {!isAuthenticated && <button onClick={() => login()}>Log in</button>} {isAuthenticated && ( <> <Link to="/profile">Profile</Link> <button onClick={() => logout()}>Log out</button> </> )} </div> ); };
  106. @samjulien const NavBar = () => { const { isAuthenticated,

    login, logout } = useAuth(); return ( <div> <Link to="/">Home</Link>{" "} {!isAuthenticated && <button onClick={() => login()}>Log in</button>} {isAuthenticated && ( <> <Link to="/profile">Profile</Link> <button onClick={() => logout()}>Log out</button> </> )} </div> ); };
  107. @samjulien const NavBar = () => { const { isAuthenticated,

    login, logout } = useAuth(); return ( <div> <Link to="/">Home</Link>{" "} {!isAuthenticated && <button onClick={() => login()}>Log in</button>} {isAuthenticated && ( <> <Link to="/profile">Profile</Link> <button onClick={() => logout()}>Log out</button> </> )} </div> ); };
  108. @samjulien const NavBar = () => { const { isAuthenticated,

    login, logout } = useAuth(); return ( <div> <Link to="/">Home</Link>{" "} {!isAuthenticated && <button onClick={() => login()}>Log in</button>} {isAuthenticated && ( <> <Link to="/profile">Profile</Link> <button onClick={() => logout()}>Log out</button> </> )} </div> ); };
  109. @samjulien const NavBar = () => { const { isAuthenticated,

    login, logout } = useAuth(); return ( <div> <Link to="/">Home</Link>{" "} {!isAuthenticated && <button onClick={() => login()}>Log in</button>} {isAuthenticated && ( <> <Link to="/profile">Profile</Link> <button onClick={() => logout()}>Log out</button> </> )} </div> ); };
  110. What if we use this in a Gatsby page? @samjulien

  111. @samjulien export default () => { const { loading }

    = useAuth(); if (loading) { return <div>Loading...</div>; } return ( <div> <NavBar /> <Home /> </div> ); };
  112. @samjulien export default () => { const { loading }

    = useAuth(); if (loading) { return <div>Loading...</div>; } return ( <div> <NavBar /> <Home /> </div> ); };
  113. @samjulien export default () => { const { loading }

    = useAuth(); if (loading) { return <div>Loading...</div>; } return ( <div> <NavBar /> <Home /> </div> ); };
  114. @samjulien

  115. @samjulien

  116. @samjulien

  117. @samjulien

  118. @samjulien const defaultContext = { isAuthenticated: false, user: null, token:

    null, loading: false, login: () => {}, };
  119. @samjulien

  120. @samjulien

  121. @samjulien

  122. @samjulien

  123. Build Time Runtime

  124. No browser APIs available! @samjulien

  125. Watch out for dependencies using browser APIs like window or

    document. @samjulien
  126. Watch out for dependencies using browser APIs like window or

    document. @samjulien (Spoilers: auth libraries usually do this a lot because of redirects and pop-ups.)
  127. gatsby-node.js @samjulien

  128. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  129. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  130. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  131. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  132. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  133. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  134. You may need to conditionally set values depending on if

    you’re in the browser. @samjulien
  135. @samjulien export const isBrowser = typeof window !== "undefined";

  136. @samjulien if (isBrowser) { changeWindowLocation(); }

  137. Let’s Review

  138. @samjulien

  139. Static Site Generator @samjulien

  140. Dynamic Site Framework @samjulien

  141. @samjulien +

  142. @samjulien +

  143. @samjulien

  144. Build Time Runtime

  145. Build Time Runtime

  146. Start login Handle redirect Log in to provider @samjulien

  147. Handle redirect Success Error @samjulien

  148. Success User Token @samjulien

  149. Success User Token Redirect @samjulien

  150. @samjulien

  151. Auth Utility @samjulien

  152. @samjulien Auth Utility

  153. @samjulien Store token and user in memory Auth Utility

  154. @samjulien Store token and user in memory Interact with auth

    SDK Auth Utility
  155. @samjulien Store token and user in memory Interact with auth

    SDK Handle all redirect logic Auth Utility
  156. @samjulien export const AuthContext = React.createContext(defaultContext); export const useAuth =

    () => useContext(AuthContext);
  157. @samjulien return ( <AuthContext.Provider value={{ isAuthenticated, user, token, loading, handleRedirectCallback,

    getToken: (...p) => authClient.getTokenSilently(...p), logout: (...p) => authClient.logout(...p), }} > {children} </AuthContext.Provider> );
  158. Adding Auth to Gatsby @samjulien Build Time vs. Runtime Using

    Hooks for Auth Special Gatsby Bits
  159. @samjulien const defaultContext = { isAuthenticated: false, user: null, token:

    null, loading: false, login: () => {}, };
  160. @samjulien

  161. @samjulien

  162. @samjulien

  163. @samjulien

  164. Build Time Runtime

  165. No browser APIs available! @samjulien

  166. Watch out for dependencies using browser APIs like window or

    document. @samjulien
  167. Watch out for dependencies using browser APIs like window or

    document. @samjulien (Spoilers: auth libraries usually do this a lot because of redirects and pop-ups.)
  168. @samjulien exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {

    if (stage === "build-html") { actions.setWebpackConfig({ module: { rules: [ { test: /auth-sdk/, use: loaders.null(), }, ], }, }); } };
  169. @samjulien export const isBrowser = typeof window !== "undefined";

  170. @samjulien

  171. samj.im/gatsby-auth @samjulien

  172. samj.im/gatsby-auth Thank you! @samjulien