Internet Identity Login Fails on LinkedIn Mobile App

I’m using Internet Identity (identity.ic0.app) for authentication in my dapp . The app works perfectly in regular browsers like Chrome, Firefox, and Safari. However, when I try to log in from the **LinkedIn mobile app, the authentication fails and give me this message

* When I click “Connect to Wallet” (which triggers Internet Identity login)

  • This only happens inside LinkedIn’s in-app browser. this is my use-auth page import { AuthClient } from “@dfinity/auth-client”;
    import React, { createContext, useContext, useEffect, useState } from “react”;
    import { canisterId as MercxId, createActor as createMercxActor } from “…/…/declarations/mercx_backend”;
    import { canisterId, createActor } from “…/…/declarations/icrc1_ledger_canister”;
    import { canisterId as icrcIndexCanisterId, createActor as createIndexActor } from “…/…/declarations/icrc1_index_canister”;
    import { canisterId as icpCanisterId, createActor as createIcpActor } from “…/…/declarations/icp_ledger_canister”;
    import { canisterId as tommyCanisterId, createActor as createTommyActor } from “…/…/declarations/tommy_icrc1_ledger”;
    import { canisterId as kycCanisterId, createActor as createKycActor } from “…/…/declarations/kyc”;

// Create a React Context for sharing authentication status across the component tree
const AuthContext = createContext();

// Function to determine the correct identity provider URL based on environment and browser
export const getIdentityProvider = () => {
let idpProvider;
// Safeguard against server rendering
// Check if the code is running in a browser environment
if (typeof window !== “undefined”) {
// Determine if the environment is local (not production)
const isLocal = process.env.DFX_NETWORK !== “ic”;
// Safari does not support localhost subdomains
// Check if the user’s browser is Safari to handle specific limitations
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isLocal && isSafari) {
// Safari handling for local development environment
idpProvider = http://localhost:8001/?canisterId=${process.env.CANISTER_ID_INTERNET_IDENTITY};
} else if (isLocal) {
// General handling for non-Safari browsers in local development
idpProvider = http://${process.env.CANISTER_ID_INTERNET_IDENTITY}.localhost:8001;
}
}
return idpProvider;
};

// Default options for the authentication client
export const defaultOptions = {
/**

  • @type {import(“@dfinity/auth-client”).AuthClientCreateOptions}
    /
    createOptions: {
    idleOptions: {
    // Set to true if you do not want idle functionality
    // Opt to disable idle functionality for the auth client
    disableIdle: true,
    },
    },
    /
    *
  • @type {import(“@dfinity/auth-client”).AuthClientLoginOptions}
    */
    loginOptions: {
    // Specify the identity provider determined by getIdentityProvider
    identityProvider: getIdentityProvider(),
    },
    };

/**
*

  • @param options - Options for the AuthClient
  • @param {AuthClientCreateOptions} options.createOptions - Options for the AuthClient.create() method
  • @param {AuthClientLoginOptions} options.loginOptions - Options for the AuthClient.login() method
  • @returns
    */
    // Custom hook to manage authentication state
    export const useAuthClient = (options = defaultOptions) => {
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [authClient, setAuthClient] = useState(null);
    const [identity, setIdentity] = useState(null);
    const [principal, setPrincipal] = useState(null);
    const [whoamiActor, setWhoamiActor] = useState(null);
    const [icrcIndexActor, setIcrcIndexActor] = useState(null);
    const [icpActor, setIcpActor] = useState(null);
    const [mercx_Actor, setMercxActor] = useState(null);
    const [tommy_Actor, setTommyActor] = useState(null);
    const [kycActor, setKycActor] = useState(null); // :white_check_mark: KYC actor

useEffect(() => {

  // Initialize AuthClient
  // Create an AuthClient instance when the component mounts
  AuthClient.create(options.createOptions).then(async (client) => {
    updateClient(client);
  });

}, );
// Function to handle user login
const login = () => {
authClient.login({
…options.loginOptions,
onSuccess: () => {
updateClient(authClient);

  },
});

};

// Update local state with the new auth client details
async function updateClient(client) {
const isAuthenticated = await client.isAuthenticated();
setIsAuthenticated(isAuthenticated);

const identity = client.getIdentity();
setIdentity(identity);

const principal = identity.getPrincipal();
setPrincipal(principal);

setAuthClient(client);

// Create an actor with the authenticated identity
const actor = createActor(canisterId, {
  agentOptions: {
    identity,
  },
});

setWhoamiActor(actor);
const indexActor = createIndexActor(icrcIndexCanisterId, {
  agentOptions: {
    identity,
  },
});
setIcrcIndexActor(indexActor);

const IcpActor = createIcpActor(icpCanisterId, {
  agentOptions: {
    identity,
  },
});
setIcpActor(IcpActor);

const MercxActor = createMercxActor(MercxId, {
  agentOptions: {
    identity,
  },
});
setMercxActor(MercxActor);

const tommyActor = createTommyActor(tommyCanisterId, {
  agentOptions: {
    identity,
  },
});
setTommyActor(tommyActor);

// ✅ Create the KYC Actor
const kycActor = createKycActor(kycCanisterId, {
  agentOptions: {
    identity,
  },
});
setKycActor(kycActor);

}

async function logout() {
await authClient?.logout();
await updateClient(authClient);
}

return {
isAuthenticated,
login,
logout,
authClient,
identity,
principal,
whoamiActor,
icrcIndexActor,
icpActor,
mercx_Actor,
tommy_Actor,
kycActor,// :white_check_mark: Return KYC actor

};
};

/**

  • @type {React.FC}
    */
    // Provider component to wrap the application and provide auth state via context
    export const AuthProvider = ({ children }) => {
    const auth = useAuthClient();

return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
};

//A simple hook that allows any component within the context provider to access the auth state and methods easily.
export const useAuth = () => useContext(AuthContext);

1 Like

In-app browsers don’t support tabs, as a result the dapp and Internet Identity cannot be both open (in different tabs) simultaneously.

For Internet Identity to authenticate with the dapp during the authentication process, both the dapp and Internet Identity need to be open simultaneously.

Additionally most in-app browsers don’t support WebAuthn (Passkeys), so the sign in flow would also likely break in this regard.

You can probably detect the linkedin browser as a dapp based on the useragent and suggest the user to open the dapp in a browser.

2 Likes