Repository: soofstad/react-oauth2-pkce
Pull request #142 (2024-03-03 04:36:53)

feat: fix functions names & related terminology to be imperative
## What does this pull request change?
This commit changes the `login` function to be `logIn`. It also updates any related terminology in the docs or otherwise to match.

## Why is this pull request needed?
Function names should conventionally be in the imperative form, but the login() function didn't conform to this convention.

## Issues related to this change
#115

soofstad: Thanks for the PR, I have nothing to add beyond Sebastians review.
Let's fixed that, and it's ready for merge :slightly_smiling_face: 
getsnoopy: Yeah; just been busy with work and life. I can get to this next week.
getsnoopy: Apologies for the delay. I've updated the code now.

Patch:
diff --git a/README.md b/README.md
index 3ebb5a0..c3db303 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ const authConfig: TAuthConfig = {
   tokenEndpoint: 'https://myAuthProvider.com/token',
   redirectUri: 'http://localhost:3000/',
   scope: 'someScope openid',
-  onRefreshTokenExpire: (event: TRefreshTokenExpiredEvent) => window.confirm('Session expired. Refresh page to continue using the site?') && event.login(),
+  onRefreshTokenExpire: (event: TRefreshTokenExpiredEvent) => window.confirm('Session expired. Refresh page to continue using the site?') && event.logIn(),
 }
 
 const UserInfo = (): JSX.Element => {
@@ -81,7 +81,7 @@ interface IAuthContext {
   tokenData?: TTokenData
   // Function to trigger login. 
   // If you want to use 'state', you might want to set 'clearURL' configuration parameter to 'false'.
-  login: (state?: string, additionalParameters?: { [key: string]: string | boolean | number }) => void  
+  logIn: (state?: string, additionalParameters?: { [key: string]: string | boolean | number }) => void
   // Function to trigger logout from authentication provider. You may provide optional 'state', and 'logout_hint' values.
   // See https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout for details.
   logOut: (state?: string, logoutHint?: string) => void
@@ -112,17 +112,17 @@ The `<AuthProvider>` takes a `config` object that supports these parameters;
 
 ```typescript
 type TAuthConfig = {
-  // Id of your app at the authentication provider
+  // ID of your app at the authentication provider
   clientId: string  // Required
   // URL for the authentication endpoint at the authentication provider
   authorizationEndpoint: string  // Required
   // URL for the token endpoint at the authentication provider
   tokenEndpoint: string  // Required
-  // Which URL the auth provider should redirect the user after successfull authentication/login
+  // Which URL the auth provider should redirect the user to after successful authentication/login
   redirectUri: string  // Required
   // Which scopes to request for the auth token
   scope?: string  // default: ''
-  // Optional state value. Will often make more sense to provide the state in a call to the 'login()' function
+  // Optional state value. Will often make more sense to provide the state in a call to the 'logIn()' function
   state?: string // default: null
   // Which URL to call for logging out of the auth provider
   logoutEndpoint?: string  // default: null
@@ -135,18 +135,18 @@ type TAuthConfig = {
   // user has been redirected back from the auth server
   postLogin?: () => void  // default: () => null
   // Optional callback function for the 'refreshTokenExpired' event.
-  // You likely want to display a message saying the user need to login again. A page refresh is enough.
+  // You likely want to display a message saying the user need to log in again. A page refresh is enough.
   onRefreshTokenExpire?: (event: TRefreshTokenExpiredEvent) => void  // default: undefined
   // Whether or not to decode the access token (should be set to 'false' if the access token is not a JWT (e.g. from Github))
   // If `false`, 'tokenData' will be 'undefined' from the <AuthContext>
   decodeToken?: boolean  // default: true
   // By default, the package will automatically redirect the user to the login server if not already logged in.
-  // If set to false, you need to call the "login()" function to login (e.g. with a "Login" button)
+  // If set to false, you need to call the "logIn()" function to log in (e.g. with a "Log in" button)
   autoLogin?: boolean  // default: true
   // Store login state in 'localStorage' or 'sessionStorage'
   // If set to 'session', no login state is persisted by 'react-oauth2-code-pkce` when the browser closes.
   // NOTE: Many authentication servers will keep the client logged in by cookies. You should therefore use 
-  // the 'logout()'-function to properly logout the client. Or configure your server not to issue cookies.
+  // the logOut() function to properly log out the client. Or configure your server not to issue cookies.
   storage?: 'local' | 'session'  // default: 'local'
   // Sets the prefix used when storing login state
   storageKeyPrefix?: string // default: 'ROCP_'
@@ -193,7 +193,7 @@ You might also see the error `Expected  to find a '?code=' parameter in the URL
 First of all, you should handle any errors the library throws. Usually, hinting at the user reload the page is enough.
 
 Some known causes for this is that instead of logging in at the auth provider, the user "Registers" or "Reset password" or 
-something similar instead. Any such functions should be handled outside of this library, with separate buttons/links than the Login-button.
+something similar instead. Any such functions should be handled outside of this library, with separate buttons/links than the "Log in" button.
 
 ### After redirect back from auth provider with `?code`, no token request is made
 
@@ -205,8 +205,8 @@ This could also happen if some routes in your app are not wrapped by the `<AuthP
 ### The page randomly refreshes in the middle of a session
 
 This will happen if you haven't provided a callback-function for the `onRefreshTokenExpire` config parameter, and the refresh token expires.
-You probably want to implement some kind of "alert/message/banner", saying that the session has expired and that the user needs to login again.
-Either by refreshing the page, or clicking a "Login-button".
+You probably want to implement some kind of "alert/message/banner", saying that the session has expired and that the user needs to log in again.
+Either by refreshing the page, or clicking a "Log in" button.
 
 ## Develop
 
diff --git a/examples/github-auth-provider/web-app/src/index.tsx b/examples/github-auth-provider/web-app/src/index.tsx
index 2a74107..c00fc2d 100644
--- a/examples/github-auth-provider/web-app/src/index.tsx
+++ b/examples/github-auth-provider/web-app/src/index.tsx
@@ -17,13 +17,13 @@ const authConfig: TAuthConfig = {
 }
 
 function LoginInfo(): JSX.Element {
-  const { tokenData, token, login, logOut, idToken, error }: IAuthContext = useContext(AuthContext)
+  const { tokenData, token, logIn, logOut, idToken, error }: IAuthContext = useContext(AuthContext)
 
   if (error) {
     return (
       <>
         <div style={{ color: 'red' }}>An error occurred during authentication: {error}</div>
-        <button onClick={() => logOut()}>Logout</button>
+        <button onClick={() => logOut()}>Log out</button>
       </>
     )
   }
@@ -62,12 +62,12 @@ function LoginInfo(): JSX.Element {
               {JSON.stringify(tokenData, null, 2)}
             </pre>
           </div>
-          <button onClick={() => logOut()}>Logout</button>
+          <button onClick={() => logOut()}>Log out</button>
         </>
       ) : (
         <>
           <div>You are not logged in.</div>
-          <button onClick={() => login()}>Login</button>
+          <button onClick={() => logIn()}>Log in</button>
         </>
       )}
     </>
diff --git a/examples/keycloak-auth-provider/web-app/src/index.tsx b/examples/keycloak-auth-provider/web-app/src/index.tsx
index 2452981..bf0d8a9 100644
--- a/examples/keycloak-auth-provider/web-app/src/index.tsx
+++ b/examples/keycloak-auth-provider/web-app/src/index.tsx
@@ -21,13 +21,13 @@ const authConfig: TAuthConfig = {
 }
 
 function LoginInfo(): JSX.Element {
-  const { tokenData, token, login, logOut, idToken, error }: IAuthContext = useContext(AuthContext)
+  const { tokenData, token, logIn, logOut, idToken, error }: IAuthContext = useContext(AuthContext)
 
   if (error) {
     return (
       <>
         <div style={{ color: 'red' }}>An error occurred during authentication: {error}</div>
-        <button onClick={() => logOut()}>Logout</button>
+        <button onClick={() => logOut()}>Log out</button>
       </>
     )
   }
@@ -66,12 +66,12 @@ function LoginInfo(): JSX.Element {
               {JSON.stringify(tokenData, null, 2)}
             </pre>
           </div>
-          <button onClick={() => logOut()}>Logout</button>
+          <button onClick={() => logOut()}>Log out</button>
         </>
       ) : (
         <>
           <div>You are not logged in.</div>
-          <button onClick={() => login()}>Login</button>
+          <button onClick={() => logIn()}>Log in</button>
         </>
       )}
     </>
diff --git a/examples/microsoft-auth-provider/web-app/src/index.tsx b/examples/microsoft-auth-provider/web-app/src/index.tsx
index 87c05ff..a3baf58 100644
--- a/examples/microsoft-auth-provider/web-app/src/index.tsx
+++ b/examples/microsoft-auth-provider/web-app/src/index.tsx
@@ -10,7 +10,7 @@ const authConfig: TAuthConfig = {
   tokenEndpoint: 'https://login.microsoftonline.com/d422398d-b6a5-454d-a202-7ed4c1bec457/oauth2/v2.0/token',
   redirectUri: 'http://localhost:3000/',
   onRefreshTokenExpire: (event) =>
-    window.confirm('Tokens have expired. Refresh page to continue using the site?') && event.login(),
+    window.confirm('Tokens have expired. Refresh page to continue using the site?') && event.logIn(),
   // Example to redirect back to original path after login has completed
   preLogin: () => localStorage.setItem('preLoginPath', window.location.pathname),
   postLogin: () => window.location.replace(localStorage.getItem('preLoginPath') || ''),
@@ -20,14 +20,14 @@ const authConfig: TAuthConfig = {
 }
 
 function LoginInfo(): JSX.Element {
-  const { tokenData, token, logOut, idToken, error, login }: IAuthContext = useContext(AuthContext)
+  const { tokenData, token, logOut, idToken, error, logIn }: IAuthContext = useContext(AuthContext)
 
   if (error) {
     return (
       <>
         <div style={{ color: 'red' }}>An error occurred during authentication: {error}</div>
         <button type='button' onClick={() => logOut()}>
-          Logout
+          Log out
         </button>
       </>
     )
@@ -61,7 +61,7 @@ function LoginInfo(): JSX.Element {
             <p>Welcome, John Doe!</p>
 
             <button type='button' style={{ width: '100px' }} onClick={() => logOut()}>
-              Logout
+              Log out
             </button>
 
             <p>Use this token to authenticate yourself</p>
@@ -104,8 +104,8 @@ function LoginInfo(): JSX.Element {
           >
             <p>Please login to continue</p>
 
-            <button type='button' style={{ width: '100px' }} onClick={() => login()}>
-              Login
+            <button type='button' style={{ width: '100px' }} onClick={() => logIn()}>
+              Log in
             </button>
           </div>
         </div>
diff --git a/src/AuthContext.tsx b/src/AuthContext.tsx
index 031c412..f7aafed 100644
--- a/src/AuthContext.tsx
+++ b/src/AuthContext.tsx
@@ -18,6 +18,7 @@ import { FALLBACK_EXPIRE_TIME, epochAtSecondsFromNow, epochTimeIsPast, getRefres
 export const AuthContext = createContext<IAuthContext>({
   token: '',
   login: () => null,
+  logIn: () => null,
   logOut: () => null,
   error: null,
   loginInProgress: false,
@@ -78,7 +79,7 @@ export const AuthProvider = ({ authConfig, children }: IAuthProvider) => {
     if (config?.logoutEndpoint && token) redirectToLogout(config, token, refreshToken, idToken, state, logoutHint)
   }
 
-  function login(state?: string, additionalParameters?: TPrimitiveRecord) {
+  function logIn(state?: string, additionalParameters?: TPrimitiveRecord) {
     clearStorage()
     setLoginInProgress(true)
     // TODO: Raise error on wrong state type in v2
@@ -125,18 +126,19 @@ export const AuthProvider = ({ authConfig, children }: IAuthProvider) => {
   function handleExpiredRefreshToken(initial = false): void {
     // If it's the first page load, OR there is no sessionExpire callback, we trigger a new login
     if (initial) {
-      login()
+      logIn()
       return
     }
 
     // TODO: Breaking change - remove automatic login during ongoing session
     if (!config.onRefreshTokenExpire) {
-      login()
+      logIn()
       return
     }
 
     config.onRefreshTokenExpire({
-      login,
+      login: logIn,
+      logIn,
     } as TRefreshTokenExpiredEvent)
   }
 
@@ -166,16 +168,16 @@ export const AuthProvider = ({ authConfig, children }: IAuthProvider) => {
               handleExpiredRefreshToken(initial)
               return
             }
-            // Unknown error. Set error, and login if first page load
+            // Unknown error. Set error, and log in if first page load
             console.error(error)
             setError(error.message)
-            if (initial) login()
+            if (initial) logIn()
           }
-          // Unknown error. Set error, and login if first page load
+          // Unknown error. Set error, and log in if first page load
           else if (error instanceof Error) {
             console.error(error)
             setError(error.message)
-            if (initial) login()
+            if (initial) logIn()
           }
         })
         .finally(() => {
@@ -250,7 +252,7 @@ export const AuthProvider = ({ authConfig, children }: IAuthProvider) => {
     }
 
     // First page visit
-    if (!token && config.autoLogin) return login()
+    if (!token && config.autoLogin) return logIn()
 
     // Page refresh after login has succeeded
     try {
@@ -273,7 +275,8 @@ export const AuthProvider = ({ authConfig, children }: IAuthProvider) => {
         tokenData,
         idToken,
         idTokenData,
-        login,
+        login: logIn,
+        logIn,
         logOut,
         error,
         loginInProgress,
diff --git a/src/Types.ts b/src/Types.ts
index de19bdc..81bd919 100644
--- a/src/Types.ts
+++ b/src/Types.ts
@@ -41,7 +41,9 @@ export interface IAuthProvider {
 
 export interface IAuthContext {
   token: string
+  logIn: (state?: string, additionalParameters?: TPrimitiveRecord) => void
   logOut: (state?: string, logoutHint?: string) => void
+  /** @deprecated Use `logIn` instead */
   login: (state?: string, additionalParameters?: TPrimitiveRecord) => void
   error: string | null
   tokenData?: TTokenData
@@ -81,6 +83,8 @@ export type TAuthConfig = {
 }
 
 export type TRefreshTokenExpiredEvent = {
+  logIn: () => void
+  /** @deprecated Use `logIn` instead */
   login: () => void
 }
 
diff --git a/src/index.js b/src/index.js
index ec2556f..65fc6fe 100644
--- a/src/index.js
+++ b/src/index.js
@@ -16,7 +16,7 @@ const authConfig = {
   logoutEndpoint: 'https://keycloak.ofstad.xyz/realms/master/protocol/openid-connect/logout',
   redirectUri: 'http://localhost:3000/',
   onRefreshTokenExpire: (event) =>
-    window.confirm('Tokens have expired. Refresh page to continue using the site?') && event.login(),
+    window.confirm('Tokens have expired. Refresh page to continue using the site?') && event.logIn(),
   decodeToken: true,
   scope: 'profile openid',
   // state: 'testState',
@@ -27,22 +27,22 @@ const authConfig = {
 }
 
 function LoginInfo() {
-  const { tokenData, token, idTokenData, login, logOut, error, loginInProgress, idToken } = useContext(AuthContext)
+  const { tokenData, token, idTokenData, logIn, logOut, error, loginInProgress, idToken } = useContext(AuthContext)
 
   if (loginInProgress) return null
   return (
     <>
       {error && <div style={{ color: 'red' }}>An error occurred during authentication: {error}</div>}
       <>
-        <button onClick={login}>Login</button>
-        <button onClick={() => login('customLoginState')}>Login w/state</button>
-        <button onClick={() => login('customLoginState', { scope: 'profile', something: 123 })}>
-          Login w/extra params
+        <button onClick={logIn}>Log in</button>
+        <button onClick={() => logIn('customLoginState')}>Log in w/state</button>
+        <button onClick={() => logIn('customLoginState', { scope: 'profile', something: 123 })}>
+          Log in w/extra params
         </button>
       </>
       {token ? (
         <>
-          <button onClick={() => logOut('rememberThis', idTokenData.session_state)}>Logout</button>
+          <button onClick={() => logOut('rememberThis', idTokenData.session_state)}>Log out</button>
           <span style={{ margin: '0 10px' }}>
             Access token will expire at:{' '}
             {new Date(localStorage.getItem('ROCP_tokenExpire') * 1000).toLocaleTimeString()}


Test patch:
diff --git a/tests/login.test.tsx b/tests/login.test.tsx
index 882b47d..f680df3 100644
--- a/tests/login.test.tsx
+++ b/tests/login.test.tsx
@@ -20,7 +20,7 @@ test('First page visit should redirect to auth provider for login', async () =>
   })
 })
 
-test('Attempting to login with and unsecure context should raise error', async () => {
+test('Attempting to log in with an unsecure context should raise error', async () => {
   // @ts-ignore
   window.crypto.subtle.digest = undefined
   render(
diff --git a/tests/logout.test.tsx b/tests/logout.test.tsx
index 3fcc1f1..f975996 100644
--- a/tests/logout.test.tsx
+++ b/tests/logout.test.tsx
@@ -16,7 +16,7 @@ test('Full featured logout requests', async () => {
     </AuthProvider>
   )
 
-  await user.click(screen.getByText('Logout'))
+  await user.click(screen.getByText('Log out'))
 
   await waitFor(() =>
     expect(window.location.assign).toHaveBeenCalledWith(
@@ -38,7 +38,7 @@ test('No refresh token, no logoutRedirect, logout request', async () => {
     </AuthProvider>
   )
 
-  await user.click(screen.getByText('Logout'))
+  await user.click(screen.getByText('Log out'))
 
   await waitFor(() =>
     expect(window.location.assign).toHaveBeenCalledWith(
diff --git a/tests/test-utils.tsx b/tests/test-utils.tsx
index 0f05b2f..79c439c 100644
--- a/tests/test-utils.tsx
+++ b/tests/test-utils.tsx
@@ -24,15 +24,15 @@ export const authConfig: TAuthConfig = {
 }
 
 export const AuthConsumer = () => {
-  const { tokenData, logOut, loginInProgress, idToken, idTokenData, login, token, error } = useContext(AuthContext)
+  const { tokenData, logOut, loginInProgress, idToken, idTokenData, logIn, token, error } = useContext(AuthContext)
   return (
     <>
       <div>{tokenData?.name}</div>
       <button type='button' onClick={() => logOut('logoutState')}>
-        Logout
+        Log out
       </button>
-      <button type='button' onClick={() => login('loginState')}>
-        Login
+      <button type='button' onClick={() => logIn('loginState')}>
+        Log in
       </button>
       <label aria-label={'loginInProgress'}>{JSON.stringify(loginInProgress)}</label>
       <label aria-label={'error'}>{error}</label>