Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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!

Sam Julien

May 02, 2020
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. Power Up Gatsby

    View full-size slide

  2. Power Up Gatsby with Auth!

    View full-size slide

  3. Static Site Generator
    @samjulien

    View full-size slide

  4. Dynamic Site Framework
    @samjulien

    View full-size slide

  5. Blazing Fast Web & App Framework
    @samjulien

    View full-size slide

  6. Adding Auth to Gatsby
    @samjulien

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  9. Adding Auth to Gatsby
    @samjulien
    Build Time vs. Runtime
    Using Hooks for Auth
    Special Gatsby Bits

    View full-size slide

  10. @samjulien
    Sam Julien
    @samjulien

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  13. @samjulien
    Sam Julien
    @samjulien
    Sr. Developer Advocate Engineer at Auth0
    Google Developer Expert
    Instructor for Thinkster & egghead

    View full-size slide

  14. Adding Auth to Gatsby
    @samjulien
    Build Time vs. Runtime
    Using Hooks for Auth
    Special Gatsby Bits

    View full-size slide

  15. Build Time Runtime

    View full-size slide

  16. Build Time
    Runtime

    View full-size slide

  17. Build Time
    Runtime

    View full-size slide

  18. Build Time
    Runtime


    View full-size slide

  19. No browser APIs available!
    @samjulien

    View full-size slide

  20. Build Time
    Runtime

    View full-size slide

  21. Build Time Runtime

    View full-size slide

  22. Build Time
    Runtime

    View full-size slide

  23. Build Time
    Runtime

    View full-size slide

  24. Build Time
    Runtime


    View full-size slide

  25. Build Time Runtime

    View full-size slide

  26. Build Time Runtime

    View full-size slide

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

    View full-size slide

  28. Handle redirect
    Success
    Error
    @samjulien

    View full-size slide

  29. Success
    User Token
    @samjulien

    View full-size slide

  30. Success
    User Token
    Redirect
    @samjulien

    View full-size slide

  31. Adding Auth to Gatsby
    @samjulien
    Build Time vs. Runtime
    Using Hooks for Auth
    Special Gatsby Bits

    View full-size slide

  32. Auth Utility
    @samjulien

    View full-size slide

  33. @samjulien
    Auth Utility

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. @samjulien
    Store token and user in memory
    Interact with auth SDK
    Handle all redirect logic
    Auth Utility

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  45. @samjulien
    ...

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  51. @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);
    }

    View full-size slide

  52. @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);
    }

    View full-size slide

  53. @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);
    }

    View full-size slide

  54. @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);
    }

    View full-size slide

  55. @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();
    }, []);

    View full-size slide

  56. @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();
    }, []);

    View full-size slide

  57. @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();
    }, []);

    View full-size slide

  58. @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();
    }, []);

    View full-size slide

  59. @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();
    }, []);

    View full-size slide

  60. @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();
    }, []);

    View full-size slide

  61. @samjulien
    const handleRedirectCallback = async () => {
    // handle callback
    // use hooks to set state
    };

    View full-size slide

  62. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  63. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  64. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  65. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  66. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  67. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  68. Auth Utility
    @samjulien

    View full-size slide

  69. @samjulien
    Auth Utility

    View full-size slide

  70. @samjulien
    Store token and user in memory
    Interact with auth SDK
    Handle all redirect logic
    Auth Utility

    View full-size slide

  71. Where do we use this AuthProvider?
    @samjulien

    View full-size slide

  72. Adding Auth to Gatsby
    @samjulien
    Build Time vs. Runtime
    Using Hooks for Auth
    Special Gatsby Bits

    View full-size slide

  73. Where do we use this AuthProvider?
    @samjulien

    View full-size slide

  74. gatsby-browser.js
    @samjulien

    View full-size slide

  75. export const wrapRootElement = ({ element }) => {
    return (
    // pass in any configuration here
    >
    {element}

    );
    };
    @samjulien

    View full-size slide

  76. export const wrapRootElement = ({ element }) => {
    return (
    // pass in any configuration here
    >
    {element}

    );
    };
    @samjulien

    View full-size slide

  77. export const wrapRootElement = ({ element }) => {
    return (
    // pass in any configuration here
    >
    {element}

    );
    };
    @samjulien

    View full-size slide

  78. Does our app work?
    @samjulien

    View full-size slide

  79. Maybe.
    @samjulien

    View full-size slide

  80. gatsby develop vs gatsby build
    @samjulien

    View full-size slide

  81. Build Time Runtime

    View full-size slide

  82. Build Time
    Runtime

    View full-size slide

  83. Build Time
    Runtime

    View full-size slide

  84. Build Time
    Runtime


    View full-size slide

  85. Build Time Runtime

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  88. @samjulien
    const defaultContext = {
    isAuthenticated: false,
    user: null,
    token: null,
    loading: false,
    login: () => {},
    };

    View full-size slide

  89. Why do we do this?
    @samjulien

    View full-size slide

  90. Example: NavBar Component
    @samjulien

    View full-size slide

  91. @samjulien
    const NavBar = () => {
    const { isAuthenticated, login, logout } = useAuth();
    return (

    Home{" "}
    {!isAuthenticated && login()}>Log in}
    {isAuthenticated && (
    <>
    Profile
    logout()}>Log out
    >
    )}

    );
    };

    View full-size slide

  92. @samjulien
    const NavBar = () => {
    const { isAuthenticated, login, logout } = useAuth();
    return (

    Home{" "}
    {!isAuthenticated && login()}>Log in}
    {isAuthenticated && (
    <>
    Profile
    logout()}>Log out
    >
    )}

    );
    };

    View full-size slide

  93. @samjulien
    const NavBar = () => {
    const { isAuthenticated, login, logout } = useAuth();
    return (

    Home{" "}
    {!isAuthenticated && login()}>Log in}
    {isAuthenticated && (
    <>
    Profile
    logout()}>Log out
    >
    )}

    );
    };

    View full-size slide

  94. @samjulien
    const NavBar = () => {
    const { isAuthenticated, login, logout } = useAuth();
    return (

    Home{" "}
    {!isAuthenticated && login()}>Log in}
    {isAuthenticated && (
    <>
    Profile
    logout()}>Log out
    >
    )}

    );
    };

    View full-size slide

  95. @samjulien
    const NavBar = () => {
    const { isAuthenticated, login, logout } = useAuth();
    return (

    Home{" "}
    {!isAuthenticated && login()}>Log in}
    {isAuthenticated && (
    <>
    Profile
    logout()}>Log out
    >
    )}

    );
    };

    View full-size slide

  96. What if we use this in a Gatsby page?
    @samjulien

    View full-size slide

  97. @samjulien
    export default () => {
    const { loading } = useAuth();
    if (loading) {
    return Loading...;
    }
    return (




    );
    };

    View full-size slide

  98. @samjulien
    export default () => {
    const { loading } = useAuth();
    if (loading) {
    return Loading...;
    }
    return (




    );
    };

    View full-size slide

  99. @samjulien
    export default () => {
    const { loading } = useAuth();
    if (loading) {
    return Loading...;
    }
    return (




    );
    };

    View full-size slide

  100. @samjulien
    const defaultContext = {
    isAuthenticated: false,
    user: null,
    token: null,
    loading: false,
    login: () => {},
    };

    View full-size slide

  101. Build Time Runtime

    View full-size slide

  102. No browser APIs available!
    @samjulien

    View full-size slide

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

    View full-size slide

  104. 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.)

    View full-size slide

  105. gatsby-node.js
    @samjulien

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  112. You may need to conditionally set values
    depending on if you’re in the browser.
    @samjulien

    View full-size slide

  113. @samjulien
    export const isBrowser = typeof window !== "undefined";

    View full-size slide

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

    View full-size slide

  115. Let’s Review

    View full-size slide

  116. Static Site Generator
    @samjulien

    View full-size slide

  117. Dynamic Site Framework
    @samjulien

    View full-size slide

  118. Build Time Runtime

    View full-size slide

  119. Build Time Runtime

    View full-size slide

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

    View full-size slide

  121. Handle redirect
    Success
    Error
    @samjulien

    View full-size slide

  122. Success
    User Token
    @samjulien

    View full-size slide

  123. Success
    User Token
    Redirect
    @samjulien

    View full-size slide

  124. Auth Utility
    @samjulien

    View full-size slide

  125. @samjulien
    Auth Utility

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  128. @samjulien
    Store token and user in memory
    Interact with auth SDK
    Handle all redirect logic
    Auth Utility

    View full-size slide

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

    View full-size slide

  130. @samjulien
    return (
    value={{
    isAuthenticated,
    user,
    token,
    loading,
    handleRedirectCallback,
    getToken: (...p) => authClient.getTokenSilently(...p),
    logout: (...p) => authClient.logout(...p),
    }}
    >
    {children}

    );

    View full-size slide

  131. Adding Auth to Gatsby
    @samjulien
    Build Time vs. Runtime
    Using Hooks for Auth
    Special Gatsby Bits

    View full-size slide

  132. @samjulien
    const defaultContext = {
    isAuthenticated: false,
    user: null,
    token: null,
    loading: false,
    login: () => {},
    };

    View full-size slide

  133. Build Time Runtime

    View full-size slide

  134. No browser APIs available!
    @samjulien

    View full-size slide

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

    View full-size slide

  136. 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.)

    View full-size slide

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

    View full-size slide

  138. @samjulien
    export const isBrowser = typeof window !== "undefined";

    View full-size slide

  139. samj.im/gatsby-auth
    @samjulien

    View full-size slide

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

    View full-size slide