Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions docs/react-oidc-context.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,24 @@ export const hasAuthParams: (location?: Location) => boolean;
// @public (undocumented)
export const useAuth: () => AuthContextProps;

// Warning: (ae-forgotten-export) The symbol "UseAutoSignInProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "UseAutoSignInReturn" needs to be exported by the entry point index.d.ts
//
// @public
export const useAutoSignin: (input?: UseAutoSignInProps) => UseAutoSignInReturn;
export function useAutoSignin(options: {
signinMethod: "signinPopup";
args?: SigninPopupArgs;
}): UseAutoSignInReturn;

// @public
export function useAutoSignin(options: {
signinMethod: "signinRedirect";
args?: SigninRedirectArgs;
}): UseAutoSignInReturn;

// @public
export function useAutoSignin(options?: {
args?: SigninRedirectArgs;
}): UseAutoSignInReturn;

// @public
export function withAuth<P>(Component: React_2.ComponentType<P>): React_2.ComponentType<Omit<P, keyof AuthContextProps>>;
Expand Down
72 changes: 57 additions & 15 deletions src/useAutoSignin.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,76 @@
import React from "react";
import { useAuth } from "./useAuth";
import { hasAuthParams } from "./utils";
import type { AuthContextProps } from "./AuthContext";
import type { AuthState } from "./AuthState";

type UseAutoSignInProps = {
signinMethod?: keyof Pick<AuthContextProps, "signinRedirect" | "signinPopup">;
}
import type {
SigninPopupArgs, SigninRedirectArgs,
} from "oidc-client-ts";

type UseAutoSignInReturn = Pick<AuthState, "isAuthenticated" | "isLoading" | "error">

/**
* @public
*
* Automatically attempts to sign in a user based on the provided sign-in method and authentication state.
* Automatically attempts to sign in a user using popup method.
*
* This hook manages automatic sign-in behavior for a user. It uses the popup sign-in
* method, the current authentication state, and ensures the sign-in attempt is made only once
* in the application context.
*
* Does not support the `signinResourceOwnerCredentials` method!
*
* @param options - Configuration object with `signinMethod: "signinPopup"` and optional `args` for popup-specific settings (popup window features, redirect_uri, etc.).
*
* @returns The current status of the authentication process.
*/
export function useAutoSignin(options: {
signinMethod: "signinPopup";
args?: SigninPopupArgs;
}): UseAutoSignInReturn;

/**
* @public
*
* Automatically attempts to sign in a user using redirect method.
*
* This hook manages automatic sign-in behavior for a user. It uses the specified sign-in
* This hook manages automatic sign-in behavior for a user. It uses the redirect sign-in
* method, the current authentication state, and ensures the sign-in attempt is made only once
* in the application context.
*
* Does not support the `signinResourceOwnerCredentials` method!
*
* @param options - (Optional) Configuration object for the sign-in method. Default to `{ signinMethod: "signinRedirect" }`.
* Possible values for `signinMethod` are:
* - `"signinRedirect"`: Redirects the user to the sign-in page (default).
* - `"signinPopup"`: Signs in the user through a popup.
* @param options - Configuration object with `signinMethod: "signinRedirect"` and optional `args` for redirect-specific settings (redirect_uri, state, extraQueryParams, etc.).
*
* @returns The current status of the authentication process.
*/
export const useAutoSignin = ({ signinMethod = "signinRedirect" }: UseAutoSignInProps = {}): UseAutoSignInReturn => {
export function useAutoSignin(options: {
signinMethod: "signinRedirect";
args?: SigninRedirectArgs;
}): UseAutoSignInReturn;

/**
* @public
*
* Automatically attempts to sign in a user using the default redirect method.
*
* This hook manages automatic sign-in behavior for a user. It uses the redirect sign-in
* method by default, the current authentication state, and ensures the sign-in attempt is made only once
* in the application context.
*
* Does not support the `signinResourceOwnerCredentials` method!
*
* @param options - (Optional) Configuration object. Defaults to `{ signinMethod: "signinRedirect" }`. May include optional `args` for redirect-specific settings (redirect_uri, state, extraQueryParams, etc.).
*
* @returns The current status of the authentication process.
*/
export function useAutoSignin(options?: {
args?: SigninRedirectArgs;
}): UseAutoSignInReturn;

export function useAutoSignin({ signinMethod = "signinRedirect", args }: {
signinMethod?: "signinRedirect" | "signinPopup";
args?: SigninPopupArgs | SigninRedirectArgs;
} = {}): UseAutoSignInReturn {
const auth = useAuth();
const [hasTriedSignin, setHasTriedSignin] = React.useState(false);

Expand All @@ -39,17 +81,17 @@ export const useAutoSignin = ({ signinMethod = "signinRedirect" }: UseAutoSignIn
if (shouldAttemptSignin) {
switch (signinMethod) {
case "signinPopup":
void auth.signinPopup();
void auth.signinPopup(args);
break;
case "signinRedirect":
default:
void auth.signinRedirect();
void auth.signinRedirect(args);
break;
}

setHasTriedSignin(true);
}
}, [auth, hasTriedSignin, shouldAttemptSignin, signinMethod]);
}, [args, auth, hasTriedSignin, shouldAttemptSignin, signinMethod]);

return {
isLoading: auth.isLoading,
Expand Down
73 changes: 73 additions & 0 deletions test/useAutoSignin.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,77 @@ describe("useAutoSignin", () => {
expect(UserManager.prototype.getUser).toHaveBeenCalled();
});

it("should pass args to signinRedirect when provided", async () => {
const wrapper = createWrapper({ ...settingsStub });
const redirectArgs = {
redirect_uri: "custom_redirect",
state: "custom_state",
};
const { result } = renderHook(() => useAutoSignin({
signinMethod: "signinRedirect",
args: redirectArgs,
}), { wrapper });

await waitFor(() => expect(result.current).toBeDefined());

expect(UserManager.prototype.signinRedirect).toHaveBeenCalledWith(redirectArgs);
});

it("should pass args to signinPopup when provided", async () => {
const wrapper = createWrapper({ ...settingsStub });
const popupArgs = {
redirect_uri: "custom_popup_redirect",
popupWindowFeatures: {
width: 500,
height: 600,
},
extraQueryParams: { foo: "bar" },
};
const { result } = renderHook(() => useAutoSignin({
signinMethod: "signinPopup",
args: popupArgs,
}), { wrapper });

await waitFor(() => expect(result.current).toBeDefined());

expect(UserManager.prototype.signinPopup).toHaveBeenCalledWith(popupArgs);
});

it("should pass args to signinRedirect when using default method", async () => {
const wrapper = createWrapper({ ...settingsStub });
const redirectArgs = {
redirect_uri: "default_method_redirect",
extraQueryParams: { foo: "bar" },
};
const { result } = renderHook(() => useAutoSignin({
args: redirectArgs,
}), { wrapper });

await waitFor(() => expect(result.current).toBeDefined());

expect(UserManager.prototype.signinRedirect).toHaveBeenCalledWith(redirectArgs);
});

it("should call signinRedirect without args when no args provided", async () => {
const wrapper = createWrapper({ ...settingsStub });
const { result } = renderHook(() => useAutoSignin({
signinMethod: "signinRedirect",
}), { wrapper });

await waitFor(() => expect(result.current).toBeDefined());

expect(UserManager.prototype.signinRedirect).toHaveBeenCalledWith(undefined);
});

it("should call signinPopup without args when no args provided", async () => {
const wrapper = createWrapper({ ...settingsStub });
const { result } = renderHook(() => useAutoSignin({
signinMethod: "signinPopup",
}), { wrapper });

await waitFor(() => expect(result.current).toBeDefined());

expect(UserManager.prototype.signinPopup).toHaveBeenCalledWith(undefined);
});

});