diff --git a/src/account/Fallback.tsx b/src/account/Fallback.tsx
index 8ef67146..03e07d49 100644
--- a/src/account/Fallback.tsx
+++ b/src/account/Fallback.tsx
@@ -8,6 +8,7 @@ const Password = lazy(() => import("keycloakify/account/pages/Password"));
 const Account = lazy(() => import("keycloakify/account/pages/Account"));
 const Sessions = lazy(() => import("keycloakify/account/pages/Sessions"));
 const Totp = lazy(() => import("keycloakify/account/pages/Totp"));
+const Applications = lazy(() => import("keycloakify/account/pages/Applications"));
 
 export default function Fallback(props: PageProps<KcContext, I18n>) {
     const { kcContext, ...rest } = props;
@@ -24,6 +25,8 @@ export default function Fallback(props: PageProps<KcContext, I18n>) {
                         return <Account kcContext={kcContext} {...rest} />;
                     case "totp.ftl":
                         return <Totp kcContext={kcContext} {...rest} />;
+                    case "applications.ftl":
+                        return <Applications kcContext={kcContext} {...rest} />;
                 }
                 assert<Equals<typeof kcContext, never>>(false);
             })()}
diff --git a/src/account/kcContext/KcContext.ts b/src/account/kcContext/KcContext.ts
index 1cce3b2c..ad6cedce 100644
--- a/src/account/kcContext/KcContext.ts
+++ b/src/account/kcContext/KcContext.ts
@@ -3,7 +3,7 @@ import { assert } from "tsafe/assert";
 import type { Equals } from "tsafe";
 import { type ThemeType } from "keycloakify/bin/constants";
 
-export type KcContext = KcContext.Password | KcContext.Account | KcContext.Sessions | KcContext.Totp;
+export type KcContext = KcContext.Password | KcContext.Account | KcContext.Sessions | KcContext.Totp | KcContext.Applications;
 
 export declare namespace KcContext {
     export type Common = {
@@ -180,6 +180,71 @@ export declare namespace KcContext {
         };
         stateChecker: string;
     };
+
+    export type Applications = Common & {
+        pageId: "applications.ftl";
+        features: {
+            log: boolean;
+            identityFederation: boolean;
+            authorization: boolean;
+            passwordUpdateSupported: boolean;
+        };
+        stateChecker: string;
+        applications: {
+            applications: {
+                realmRolesAvailable: { name: string; description: string }[];
+                resourceRolesAvailable: Record<
+                    string,
+                    {
+                        roleName: string;
+                        roleDescription: string;
+                        clientName: string;
+                        clientId: string;
+                    }[]
+                >;
+                additionalGrants: string[];
+                clientScopesGranted: string[];
+                effectiveUrl?: string;
+                client: {
+                    consentScreenText: string;
+                    surrogateAuthRequired: boolean;
+                    bearerOnly: boolean;
+                    id: string;
+                    protocolMappersStream: Record<string, unknown>;
+                    includeInTokenScope: boolean;
+                    redirectUris: string[];
+                    fullScopeAllowed: boolean;
+                    registeredNodes: Record<string, unknown>;
+                    enabled: boolean;
+                    clientAuthenticatorType: string;
+                    realmScopeMappingsStream: Record<string, unknown>;
+                    scopeMappingsStream: Record<string, unknown>;
+                    displayOnConsentScreen: boolean;
+                    clientId: string;
+                    rootUrl: string;
+                    authenticationFlowBindingOverrides: Record<string, unknown>;
+                    standardFlowEnabled: boolean;
+                    attributes: Record<string, unknown>;
+                    publicClient: boolean;
+                    alwaysDisplayInConsole: boolean;
+                    consentRequired: boolean;
+                    notBefore: string;
+                    rolesStream: Record<string, unknown>;
+                    protocol: string;
+                    dynamicScope: boolean;
+                    directAccessGrantsEnabled: boolean;
+                    name: string;
+                    serviceAccountsEnabled: boolean;
+                    frontchannelLogout: boolean;
+                    nodeReRegistrationTimeout: string;
+                    implicitFlowEnabled: boolean;
+                    baseUrl: string;
+                    webOrigins: string[];
+                    realm: Record<string, unknown>;
+                };
+            }[];
+        };
+    };
 }
 
 {
diff --git a/src/account/kcContext/kcContextMocks.ts b/src/account/kcContext/kcContextMocks.ts
index 6f52d8d3..1afd18f3 100644
--- a/src/account/kcContext/kcContextMocks.ts
+++ b/src/account/kcContext/kcContextMocks.ts
@@ -198,7 +198,7 @@ export const kcContextMocks: KcContext[] = [
                 }
             ]
         },
-        "stateChecker": ""
+        "stateChecker": "g6WB1FaYnKotTkiy7ZrlxvFztSqS0U8jvHsOOOb2z4g"
     }),
     id<KcContext.Totp>({
         ...kcContextCommonMock,
diff --git a/src/account/pages/Applications.tsx b/src/account/pages/Applications.tsx
new file mode 100644
index 00000000..c1a51da4
--- /dev/null
+++ b/src/account/pages/Applications.tsx
@@ -0,0 +1,138 @@
+import { clsx } from "keycloakify/tools/clsx";
+import type { PageProps } from "keycloakify/account/pages/PageProps";
+import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
+
+function isArrayWithEmptyObject(variable: any): boolean {
+    return Array.isArray(variable) && variable.length === 1 && typeof variable[0] === "object" && Object.keys(variable[0]).length === 0;
+}
+
+export default function Applications(props: PageProps<Extract<KcContext, { pageId: "applications.ftl" }>, I18n>) {
+    const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
+
+    const { getClassName } = useGetClassName({
+        doUseDefaultCss,
+        classes
+    });
+
+    const {
+        url,
+        applications: { applications },
+        stateChecker
+    } = kcContext;
+
+    const { msg, advancedMsg } = i18n;
+
+    return (
+        <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="applications">
+            <div className="row">
+                <div className="col-md-10">
+                    <h2>{msg("applicationsHtmlTitle")}</h2>
+                </div>
+
+                <form action={url.applicationsUrl} method="post">
+                    <input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
+                    <input type="hidden" id="referrer" name="referrer" value={stateChecker} />
+
+                    <table className="table table-striped table-bordered">
+                        <thead>
+                            <tr>
+                                <td>{msg("application")}</td>
+                                <td>{msg("availableRoles")}</td>
+                                <td>{msg("grantedPermissions")}</td>
+                                <td>{msg("additionalGrants")}</td>
+                                <td>{msg("action")}</td>
+                            </tr>
+                        </thead>
+
+                        <tbody>
+                            {applications.map(application => (
+                                <tr key={application.client.clientId}>
+                                    <td>
+                                        {application.effectiveUrl && (
+                                            <a href={application.effectiveUrl}>
+                                                {(application.client.name && advancedMsg(application.client.name)) || application.client.clientId}
+                                            </a>
+                                        )}
+                                        {!application.effectiveUrl &&
+                                            ((application.client.name && advancedMsg(application.client.name)) || application.client.clientId)}
+                                    </td>
+
+                                    <td>
+                                        {!isArrayWithEmptyObject(application.realmRolesAvailable) &&
+                                            application.realmRolesAvailable.map(role => (
+                                                <span key={role.name}>
+                                                    {role.description ? advancedMsg(role.description) : advancedMsg(role.name)}
+                                                    {role !== application.realmRolesAvailable[application.realmRolesAvailable.length - 1] && ", "}
+                                                </span>
+                                            ))}
+                                        {!isArrayWithEmptyObject(application.realmRolesAvailable) && application.resourceRolesAvailable && ", "}
+                                        {application.resourceRolesAvailable &&
+                                            Object.keys(application.resourceRolesAvailable).map(resource => (
+                                                <span key={resource}>
+                                                    {!isArrayWithEmptyObject(application.realmRolesAvailable) && ", "}
+                                                    {application.resourceRolesAvailable[resource].map(clientRole => (
+                                                        <span key={clientRole.roleName}>
+                                                            {clientRole.roleDescription
+                                                                ? advancedMsg(clientRole.roleDescription)
+                                                                : advancedMsg(clientRole.roleName)}
+                                                            {msg("inResource")}{" "}
+                                                            <strong>
+                                                                {clientRole.clientName ? advancedMsg(clientRole.clientName) : clientRole.clientId}
+                                                            </strong>
+                                                            {clientRole !==
+                                                                application.resourceRolesAvailable[resource][
+                                                                    application.resourceRolesAvailable[resource].length - 1
+                                                                ] && ", "}
+                                                        </span>
+                                                    ))}
+                                                </span>
+                                            ))}
+                                    </td>
+
+                                    <td>
+                                        {application.client.consentRequired ? (
+                                            application.clientScopesGranted.map(claim => (
+                                                <span key={claim}>
+                                                    {advancedMsg(claim)}
+                                                    {claim !== application.clientScopesGranted[application.clientScopesGranted.length - 1] && ", "}
+                                                </span>
+                                            ))
+                                        ) : (
+                                            <strong>{msg("fullAccess")}</strong>
+                                        )}
+                                    </td>
+
+                                    <td>
+                                        {application.additionalGrants.map(grant => (
+                                            <span key={grant}>
+                                                {advancedMsg(grant)}
+                                                {grant !== application.additionalGrants[application.additionalGrants.length - 1] && ", "}
+                                            </span>
+                                        ))}
+                                    </td>
+
+                                    <td>
+                                        {(application.client.consentRequired && application.clientScopesGranted.length > 0) ||
+                                        application.additionalGrants.length > 0 ? (
+                                            <button
+                                                type="submit"
+                                                className={clsx(getClassName("kcButtonPrimaryClass"), getClassName("kcButtonClass"))}
+                                                id={`revoke-${application.client.clientId}`}
+                                                name="clientId"
+                                                value={application.client.id}
+                                            >
+                                                {msg("revoke")}
+                                            </button>
+                                        ) : null}
+                                    </td>
+                                </tr>
+                            ))}
+                        </tbody>
+                    </table>
+                </form>
+            </div>
+        </Template>
+    );
+}
diff --git a/src/bin/keycloakify/generateFtl/pageId.ts b/src/bin/keycloakify/generateFtl/pageId.ts
index 3688198c..3188198f 100644
--- a/src/bin/keycloakify/generateFtl/pageId.ts
+++ b/src/bin/keycloakify/generateFtl/pageId.ts
@@ -27,7 +27,7 @@ export const loginThemePageIds = [
     "saml-post-form.ftl"
 ] as const;
 
-export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl"] as const;
+export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl", "applications.ftl"] as const;
 
 export type LoginThemePageId = (typeof loginThemePageIds)[number];
 export type AccountThemePageId = (typeof accountThemePageIds)[number];