Our Personal Data Server from scratch! tranquil.farm
atproto pds rust postgresql fun oauth

refactor(frontend): simplify API client and delete obsolete auth routes #72

merged opened by oyster.cafe targeting main from refactor/frontend-deletions
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:3fwecdnvtcscjnrx2p4n7alz/sh.tangled.repo.pull/3mhg53l5va222
+12 -894
Diff #2
+12 -634
frontend/src/lib/api.ts
··· 441 441 params: { 442 442 did: Did; 443 443 handle: Handle; 444 - email: EmailAddress; 444 + email?: EmailAddress; 445 445 password: string; 446 446 inviteCode?: string; 447 + verificationChannel?: string; 448 + discordUsername?: string; 449 + telegramUsername?: string; 450 + signalUsername?: string; 447 451 }, 448 452 ): Promise<Session> { 449 453 const url = `${API_BASE}/com.atproto.server.createAccount`; ··· 459 463 email: params.email, 460 464 password: params.password, 461 465 inviteCode: params.inviteCode, 466 + verificationChannel: params.verificationChannel, 467 + discordUsername: params.discordUsername, 468 + telegramUsername: params.telegramUsername, 469 + signalUsername: params.signalUsername, 462 470 }), 463 471 }); 464 472 const data = await response.json(); ··· 1231 1239 }, 1232 1240 1233 1241 resendMigrationVerification( 1234 - email: EmailAddress, 1242 + channel: string, 1243 + identifier: string, 1235 1244 ): Promise<ResendMigrationVerificationResponse> { 1236 1245 return xrpc("com.atproto.server.resendMigrationVerification", { 1237 1246 method: "POST", 1238 - body: { email }, 1247 + body: { channel, identifier }, 1239 1248 }); 1240 1249 }, 1241 1250 ··· 1509 1518 }, 1510 1519 1511 1520 }; 1512 - 1513 - export const typedApi = { 1514 - createSession( 1515 - identifier: string, 1516 - password: string, 1517 - ): Promise<Result<Session, ApiError>> { 1518 - return xrpcResult<Session>("com.atproto.server.createSession", { 1519 - method: "POST", 1520 - body: { identifier, password }, 1521 - }).then((r) => r.ok ? ok(castSession(r.value)) : r); 1522 - }, 1523 - 1524 - getSession(token: AccessToken): Promise<Result<Session, ApiError>> { 1525 - return xrpcResult<Session>("com.atproto.server.getSession", { token }) 1526 - .then((r) => r.ok ? ok(castSession(r.value)) : r); 1527 - }, 1528 - 1529 - refreshSession(refreshJwt: RefreshToken): Promise<Result<Session, ApiError>> { 1530 - return xrpcResult<Session>("com.atproto.server.refreshSession", { 1531 - method: "POST", 1532 - token: refreshJwt, 1533 - }).then((r) => r.ok ? ok(castSession(r.value)) : r); 1534 - }, 1535 - 1536 - describeServer(): Promise<Result<ServerDescription, ApiError>> { 1537 - return xrpcResult("com.atproto.server.describeServer"); 1538 - }, 1539 - 1540 - listAppPasswords( 1541 - token: AccessToken, 1542 - ): Promise<Result<{ passwords: AppPassword[] }, ApiError>> { 1543 - return xrpcResult("com.atproto.server.listAppPasswords", { token }); 1544 - }, 1545 - 1546 - createAppPassword( 1547 - token: AccessToken, 1548 - name: string, 1549 - scopes?: string, 1550 - ): Promise<Result<CreatedAppPassword, ApiError>> { 1551 - return xrpcResult("com.atproto.server.createAppPassword", { 1552 - method: "POST", 1553 - token, 1554 - body: { name, scopes }, 1555 - }); 1556 - }, 1557 - 1558 - revokeAppPassword( 1559 - token: AccessToken, 1560 - name: string, 1561 - ): Promise<Result<void, ApiError>> { 1562 - return xrpcResult<void>("com.atproto.server.revokeAppPassword", { 1563 - method: "POST", 1564 - token, 1565 - body: { name }, 1566 - }); 1567 - }, 1568 - 1569 - listSessions( 1570 - token: AccessToken, 1571 - ): Promise<Result<ListSessionsResponse, ApiError>> { 1572 - return xrpcResult("_account.listSessions", { token }); 1573 - }, 1574 - 1575 - revokeSession( 1576 - token: AccessToken, 1577 - sessionId: string, 1578 - ): Promise<Result<void, ApiError>> { 1579 - return xrpcResult<void>("_account.revokeSession", { 1580 - method: "POST", 1581 - token, 1582 - body: { sessionId }, 1583 - }); 1584 - }, 1585 - 1586 - getTotpStatus(token: AccessToken): Promise<Result<TotpStatus, ApiError>> { 1587 - return xrpcResult("com.atproto.server.getTotpStatus", { token }); 1588 - }, 1589 - 1590 - createTotpSecret(token: AccessToken): Promise<Result<TotpSecret, ApiError>> { 1591 - return xrpcResult("com.atproto.server.createTotpSecret", { 1592 - method: "POST", 1593 - token, 1594 - }); 1595 - }, 1596 - 1597 - enableTotp( 1598 - token: AccessToken, 1599 - code: string, 1600 - ): Promise<Result<EnableTotpResponse, ApiError>> { 1601 - return xrpcResult("com.atproto.server.enableTotp", { 1602 - method: "POST", 1603 - token, 1604 - body: { code }, 1605 - }); 1606 - }, 1607 - 1608 - disableTotp( 1609 - token: AccessToken, 1610 - password: string, 1611 - code: string, 1612 - ): Promise<Result<SuccessResponse, ApiError>> { 1613 - return xrpcResult("com.atproto.server.disableTotp", { 1614 - method: "POST", 1615 - token, 1616 - body: { password, code }, 1617 - }); 1618 - }, 1619 - 1620 - listPasskeys( 1621 - token: AccessToken, 1622 - ): Promise<Result<ListPasskeysResponse, ApiError>> { 1623 - return xrpcResult("com.atproto.server.listPasskeys", { token }); 1624 - }, 1625 - 1626 - deletePasskey( 1627 - token: AccessToken, 1628 - id: string, 1629 - ): Promise<Result<void, ApiError>> { 1630 - return xrpcResult<void>("com.atproto.server.deletePasskey", { 1631 - method: "POST", 1632 - token, 1633 - body: { id }, 1634 - }); 1635 - }, 1636 - 1637 - listTrustedDevices( 1638 - token: AccessToken, 1639 - ): Promise<Result<ListTrustedDevicesResponse, ApiError>> { 1640 - return xrpcResult("_account.listTrustedDevices", { token }); 1641 - }, 1642 - 1643 - getReauthStatus(token: AccessToken): Promise<Result<ReauthStatus, ApiError>> { 1644 - return xrpcResult("_account.getReauthStatus", { token }); 1645 - }, 1646 - 1647 - getNotificationPrefs( 1648 - token: AccessToken, 1649 - ): Promise<Result<NotificationPrefs, ApiError>> { 1650 - return xrpcResult("_account.getNotificationPrefs", { token }); 1651 - }, 1652 - 1653 - updateHandle( 1654 - token: AccessToken, 1655 - handle: Handle, 1656 - ): Promise<Result<void, ApiError>> { 1657 - return xrpcResult<void>("com.atproto.identity.updateHandle", { 1658 - method: "POST", 1659 - token, 1660 - body: { handle }, 1661 - }); 1662 - }, 1663 - 1664 - describeRepo( 1665 - token: AccessToken, 1666 - repo: Did, 1667 - ): Promise<Result<RepoDescription, ApiError>> { 1668 - return xrpcResult("com.atproto.repo.describeRepo", { 1669 - token, 1670 - params: { repo }, 1671 - }); 1672 - }, 1673 - 1674 - listRecords( 1675 - token: AccessToken, 1676 - repo: Did, 1677 - collection: Nsid, 1678 - options?: { limit?: number; cursor?: string; reverse?: boolean }, 1679 - ): Promise<Result<ListRecordsResponse, ApiError>> { 1680 - const params: Record<string, string> = { repo, collection }; 1681 - if (options?.limit) params.limit = String(options.limit); 1682 - if (options?.cursor) params.cursor = options.cursor; 1683 - if (options?.reverse) params.reverse = "true"; 1684 - return xrpcResult("com.atproto.repo.listRecords", { token, params }); 1685 - }, 1686 - 1687 - getRecord( 1688 - token: AccessToken, 1689 - repo: Did, 1690 - collection: Nsid, 1691 - rkey: Rkey, 1692 - ): Promise<Result<RecordResponse, ApiError>> { 1693 - return xrpcResult("com.atproto.repo.getRecord", { 1694 - token, 1695 - params: { repo, collection, rkey }, 1696 - }); 1697 - }, 1698 - 1699 - deleteRecord( 1700 - token: AccessToken, 1701 - repo: Did, 1702 - collection: Nsid, 1703 - rkey: Rkey, 1704 - ): Promise<Result<void, ApiError>> { 1705 - return xrpcResult<void>("com.atproto.repo.deleteRecord", { 1706 - method: "POST", 1707 - token, 1708 - body: { repo, collection, rkey }, 1709 - }); 1710 - }, 1711 - 1712 - searchAccounts( 1713 - token: AccessToken, 1714 - options?: { handle?: string; cursor?: string; limit?: number }, 1715 - ): Promise<Result<SearchAccountsResponse, ApiError>> { 1716 - const params: Record<string, string> = {}; 1717 - if (options?.handle) params.handle = options.handle; 1718 - if (options?.cursor) params.cursor = options.cursor; 1719 - if (options?.limit) params.limit = String(options.limit); 1720 - return xrpcResult("com.atproto.admin.searchAccounts", { token, params }); 1721 - }, 1722 - 1723 - getAccountInfo( 1724 - token: AccessToken, 1725 - did: Did, 1726 - ): Promise<Result<AccountInfo, ApiError>> { 1727 - return xrpcResult("com.atproto.admin.getAccountInfo", { 1728 - token, 1729 - params: { did }, 1730 - }); 1731 - }, 1732 - 1733 - getServerStats(token: AccessToken): Promise<Result<ServerStats, ApiError>> { 1734 - return xrpcResult("_admin.getServerStats", { token }); 1735 - }, 1736 - 1737 - getDidDocument(token: AccessToken): Promise<Result<DidDocument, ApiError>> { 1738 - return xrpcResult("_account.getDidDocument", { token }); 1739 - }, 1740 - 1741 - deleteSession(token: AccessToken): Promise<Result<void, ApiError>> { 1742 - return xrpcResult<void>("com.atproto.server.deleteSession", { 1743 - method: "POST", 1744 - token, 1745 - }); 1746 - }, 1747 - 1748 - revokeAllSessions( 1749 - token: AccessToken, 1750 - ): Promise<Result<{ revokedCount: number }, ApiError>> { 1751 - return xrpcResult("_account.revokeAllSessions", { 1752 - method: "POST", 1753 - token, 1754 - }); 1755 - }, 1756 - 1757 - getAccountInviteCodes( 1758 - token: AccessToken, 1759 - ): Promise<Result<{ codes: InviteCodeInfo[] }, ApiError>> { 1760 - return xrpcResult("com.atproto.server.getAccountInviteCodes", { token }); 1761 - }, 1762 - 1763 - createInviteCode( 1764 - token: AccessToken, 1765 - useCount: number = 1, 1766 - ): Promise<Result<{ code: string }, ApiError>> { 1767 - return xrpcResult("com.atproto.server.createInviteCode", { 1768 - method: "POST", 1769 - token, 1770 - body: { useCount }, 1771 - }); 1772 - }, 1773 - 1774 - changePassword( 1775 - token: AccessToken, 1776 - currentPassword: string, 1777 - newPassword: string, 1778 - ): Promise<Result<void, ApiError>> { 1779 - return xrpcResult<void>("_account.changePassword", { 1780 - method: "POST", 1781 - token, 1782 - body: { currentPassword, newPassword }, 1783 - }); 1784 - }, 1785 - 1786 - getPasswordStatus( 1787 - token: AccessToken, 1788 - ): Promise<Result<PasswordStatus, ApiError>> { 1789 - return xrpcResult("_account.getPasswordStatus", { token }); 1790 - }, 1791 - 1792 - getServerConfig(): Promise<Result<ServerConfig, ApiError>> { 1793 - return xrpcResult("_server.getConfig"); 1794 - }, 1795 - 1796 - getLegacyLoginPreference( 1797 - token: AccessToken, 1798 - ): Promise<Result<LegacyLoginPreference, ApiError>> { 1799 - return xrpcResult("_account.getLegacyLoginPreference", { token }); 1800 - }, 1801 - 1802 - updateLegacyLoginPreference( 1803 - token: AccessToken, 1804 - allowLegacyLogin: boolean, 1805 - ): Promise<Result<UpdateLegacyLoginResponse, ApiError>> { 1806 - return xrpcResult("_account.updateLegacyLoginPreference", { 1807 - method: "POST", 1808 - token, 1809 - body: { allowLegacyLogin }, 1810 - }); 1811 - }, 1812 - 1813 - getNotificationHistory( 1814 - token: AccessToken, 1815 - ): Promise<Result<NotificationHistoryResponse, ApiError>> { 1816 - return xrpcResult("_account.getNotificationHistory", { token }); 1817 - }, 1818 - 1819 - updateNotificationPrefs( 1820 - token: AccessToken, 1821 - prefs: { 1822 - preferredChannel?: string; 1823 - discordUsername?: string; 1824 - telegramUsername?: string; 1825 - signalUsername?: string; 1826 - }, 1827 - ): Promise<Result<UpdateNotificationPrefsResponse, ApiError>> { 1828 - return xrpcResult("_account.updateNotificationPrefs", { 1829 - method: "POST", 1830 - token, 1831 - body: prefs, 1832 - }); 1833 - }, 1834 - 1835 - revokeTrustedDevice( 1836 - token: AccessToken, 1837 - deviceId: string, 1838 - ): Promise<Result<SuccessResponse, ApiError>> { 1839 - return xrpcResult("_account.revokeTrustedDevice", { 1840 - method: "POST", 1841 - token, 1842 - body: { deviceId }, 1843 - }); 1844 - }, 1845 - 1846 - updateTrustedDevice( 1847 - token: AccessToken, 1848 - deviceId: string, 1849 - friendlyName: string, 1850 - ): Promise<Result<SuccessResponse, ApiError>> { 1851 - return xrpcResult("_account.updateTrustedDevice", { 1852 - method: "POST", 1853 - token, 1854 - body: { deviceId, friendlyName }, 1855 - }); 1856 - }, 1857 - 1858 - reauthPassword( 1859 - token: AccessToken, 1860 - password: string, 1861 - ): Promise<Result<ReauthResponse, ApiError>> { 1862 - return xrpcResult("_account.reauthPassword", { 1863 - method: "POST", 1864 - token, 1865 - body: { password }, 1866 - }); 1867 - }, 1868 - 1869 - reauthTotp( 1870 - token: AccessToken, 1871 - code: string, 1872 - ): Promise<Result<ReauthResponse, ApiError>> { 1873 - return xrpcResult("_account.reauthTotp", { 1874 - method: "POST", 1875 - token, 1876 - body: { code }, 1877 - }); 1878 - }, 1879 - 1880 - reauthPasskeyStart( 1881 - token: AccessToken, 1882 - ): Promise<Result<ReauthPasskeyStartResponse, ApiError>> { 1883 - return xrpcResult("_account.reauthPasskeyStart", { 1884 - method: "POST", 1885 - token, 1886 - }); 1887 - }, 1888 - 1889 - reauthPasskeyFinish( 1890 - token: AccessToken, 1891 - credential: unknown, 1892 - ): Promise<Result<ReauthResponse, ApiError>> { 1893 - return xrpcResult("_account.reauthPasskeyFinish", { 1894 - method: "POST", 1895 - token, 1896 - body: { credential }, 1897 - }); 1898 - }, 1899 - 1900 - confirmSignup( 1901 - did: Did, 1902 - verificationCode: string, 1903 - ): Promise<Result<ConfirmSignupResult, ApiError>> { 1904 - return xrpcResult("com.atproto.server.confirmSignup", { 1905 - method: "POST", 1906 - body: { did, verificationCode }, 1907 - }); 1908 - }, 1909 - 1910 - resendVerification( 1911 - did: Did, 1912 - ): Promise<Result<{ success: boolean }, ApiError>> { 1913 - return xrpcResult("com.atproto.server.resendVerification", { 1914 - method: "POST", 1915 - body: { did }, 1916 - }); 1917 - }, 1918 - 1919 - requestEmailUpdate( 1920 - token: AccessToken, 1921 - ): Promise<Result<EmailUpdateResponse, ApiError>> { 1922 - return xrpcResult("com.atproto.server.requestEmailUpdate", { 1923 - method: "POST", 1924 - token, 1925 - }); 1926 - }, 1927 - 1928 - updateEmail( 1929 - token: AccessToken, 1930 - email: string, 1931 - emailToken?: string, 1932 - ): Promise<Result<void, ApiError>> { 1933 - return xrpcResult<void>("com.atproto.server.updateEmail", { 1934 - method: "POST", 1935 - token, 1936 - body: { email, token: emailToken }, 1937 - }); 1938 - }, 1939 - 1940 - requestAccountDelete(token: AccessToken): Promise<Result<void, ApiError>> { 1941 - return xrpcResult<void>("com.atproto.server.requestAccountDelete", { 1942 - method: "POST", 1943 - token, 1944 - }); 1945 - }, 1946 - 1947 - deleteAccount( 1948 - did: Did, 1949 - password: string, 1950 - deleteToken: string, 1951 - ): Promise<Result<void, ApiError>> { 1952 - return xrpcResult<void>("com.atproto.server.deleteAccount", { 1953 - method: "POST", 1954 - body: { did, password, token: deleteToken }, 1955 - }); 1956 - }, 1957 - 1958 - updateDidDocument( 1959 - token: AccessToken, 1960 - params: { 1961 - verificationMethods?: VerificationMethod[]; 1962 - alsoKnownAs?: string[]; 1963 - serviceEndpoint?: string; 1964 - }, 1965 - ): Promise<Result<SuccessResponse, ApiError>> { 1966 - return xrpcResult("_account.updateDidDocument", { 1967 - method: "POST", 1968 - token, 1969 - body: params, 1970 - }); 1971 - }, 1972 - 1973 - deactivateAccount( 1974 - token: AccessToken, 1975 - deleteAfter?: string, 1976 - ): Promise<Result<void, ApiError>> { 1977 - return xrpcResult<void>("com.atproto.server.deactivateAccount", { 1978 - method: "POST", 1979 - token, 1980 - body: { deleteAfter }, 1981 - }); 1982 - }, 1983 - 1984 - activateAccount(token: AccessToken): Promise<Result<void, ApiError>> { 1985 - return xrpcResult<void>("com.atproto.server.activateAccount", { 1986 - method: "POST", 1987 - token, 1988 - }); 1989 - }, 1990 - 1991 - createRecord( 1992 - token: AccessToken, 1993 - repo: Did, 1994 - collection: Nsid, 1995 - record: unknown, 1996 - rkey?: Rkey, 1997 - ): Promise<Result<CreateRecordResponse, ApiError>> { 1998 - return xrpcResult("com.atproto.repo.createRecord", { 1999 - method: "POST", 2000 - token, 2001 - body: { repo, collection, record, rkey }, 2002 - }); 2003 - }, 2004 - 2005 - putRecord( 2006 - token: AccessToken, 2007 - repo: Did, 2008 - collection: Nsid, 2009 - rkey: Rkey, 2010 - record: unknown, 2011 - ): Promise<Result<CreateRecordResponse, ApiError>> { 2012 - return xrpcResult("com.atproto.repo.putRecord", { 2013 - method: "POST", 2014 - token, 2015 - body: { repo, collection, rkey, record }, 2016 - }); 2017 - }, 2018 - 2019 - getInviteCodes( 2020 - token: AccessToken, 2021 - options?: { sort?: "recent" | "usage"; cursor?: string; limit?: number }, 2022 - ): Promise<Result<GetInviteCodesResponse, ApiError>> { 2023 - const params: Record<string, string> = {}; 2024 - if (options?.sort) params.sort = options.sort; 2025 - if (options?.cursor) params.cursor = options.cursor; 2026 - if (options?.limit) params.limit = String(options.limit); 2027 - return xrpcResult("com.atproto.admin.getInviteCodes", { token, params }); 2028 - }, 2029 - 2030 - disableAccountInvites( 2031 - token: AccessToken, 2032 - account: Did, 2033 - ): Promise<Result<void, ApiError>> { 2034 - return xrpcResult<void>("com.atproto.admin.disableAccountInvites", { 2035 - method: "POST", 2036 - token, 2037 - body: { account }, 2038 - }); 2039 - }, 2040 - 2041 - enableAccountInvites( 2042 - token: AccessToken, 2043 - account: Did, 2044 - ): Promise<Result<void, ApiError>> { 2045 - return xrpcResult<void>("com.atproto.admin.enableAccountInvites", { 2046 - method: "POST", 2047 - token, 2048 - body: { account }, 2049 - }); 2050 - }, 2051 - 2052 - adminDeleteAccount( 2053 - token: AccessToken, 2054 - did: Did, 2055 - ): Promise<Result<void, ApiError>> { 2056 - return xrpcResult<void>("com.atproto.admin.deleteAccount", { 2057 - method: "POST", 2058 - token, 2059 - body: { did }, 2060 - }); 2061 - }, 2062 - 2063 - startPasskeyRegistration( 2064 - token: AccessToken, 2065 - friendlyName?: string, 2066 - ): Promise<Result<StartPasskeyRegistrationResponse, ApiError>> { 2067 - return xrpcResult("com.atproto.server.startPasskeyRegistration", { 2068 - method: "POST", 2069 - token, 2070 - body: { friendlyName }, 2071 - }); 2072 - }, 2073 - 2074 - finishPasskeyRegistration( 2075 - token: AccessToken, 2076 - credential: unknown, 2077 - friendlyName?: string, 2078 - ): Promise<Result<FinishPasskeyRegistrationResponse, ApiError>> { 2079 - return xrpcResult("com.atproto.server.finishPasskeyRegistration", { 2080 - method: "POST", 2081 - token, 2082 - body: { credential, friendlyName }, 2083 - }); 2084 - }, 2085 - 2086 - updatePasskey( 2087 - token: AccessToken, 2088 - id: string, 2089 - friendlyName: string, 2090 - ): Promise<Result<void, ApiError>> { 2091 - return xrpcResult<void>("com.atproto.server.updatePasskey", { 2092 - method: "POST", 2093 - token, 2094 - body: { id, friendlyName }, 2095 - }); 2096 - }, 2097 - 2098 - regenerateBackupCodes( 2099 - token: AccessToken, 2100 - password: string, 2101 - code: string, 2102 - ): Promise<Result<RegenerateBackupCodesResponse, ApiError>> { 2103 - return xrpcResult("com.atproto.server.regenerateBackupCodes", { 2104 - method: "POST", 2105 - token, 2106 - body: { password, code }, 2107 - }); 2108 - }, 2109 - 2110 - updateLocale( 2111 - token: AccessToken, 2112 - preferredLocale: string, 2113 - ): Promise<Result<UpdateLocaleResponse, ApiError>> { 2114 - return xrpcResult("_account.updateLocale", { 2115 - method: "POST", 2116 - token, 2117 - body: { preferredLocale }, 2118 - }); 2119 - }, 2120 - 2121 - confirmChannelVerification( 2122 - token: AccessToken, 2123 - channel: string, 2124 - identifier: string, 2125 - code: string, 2126 - ): Promise<Result<SuccessResponse, ApiError>> { 2127 - return xrpcResult("_account.confirmChannelVerification", { 2128 - method: "POST", 2129 - token, 2130 - body: { channel, identifier, code }, 2131 - }); 2132 - }, 2133 - 2134 - removePassword( 2135 - token: AccessToken, 2136 - ): Promise<Result<SuccessResponse, ApiError>> { 2137 - return xrpcResult("_account.removePassword", { 2138 - method: "POST", 2139 - token, 2140 - }); 2141 - }, 2142 - };
-112
frontend/src/routes/OAuth2FA.svelte
··· 1 - <script lang="ts"> 2 - import { navigate, routes } from '../lib/router.svelte' 3 - import { _ } from '../lib/i18n' 4 - 5 - let code = $state('') 6 - let submitting = $state(false) 7 - let error = $state<string | null>(null) 8 - 9 - function getRequestUri(): string | null { 10 - const params = new URLSearchParams(window.location.search) 11 - return params.get('request_uri') 12 - } 13 - 14 - function getChannel(): string { 15 - const params = new URLSearchParams(window.location.search) 16 - return params.get('channel') || 'email' 17 - } 18 - 19 - async function handleSubmit(e: Event) { 20 - e.preventDefault() 21 - const requestUri = getRequestUri() 22 - if (!requestUri) { 23 - error = $_('oauth.twoFactorCode.errors.missingRequestUri') 24 - return 25 - } 26 - 27 - submitting = true 28 - error = null 29 - 30 - try { 31 - const response = await fetch('/oauth/authorize/2fa', { 32 - method: 'POST', 33 - headers: { 34 - 'Content-Type': 'application/json', 35 - 'Accept': 'application/json' 36 - }, 37 - body: JSON.stringify({ 38 - request_uri: requestUri, 39 - code: code.trim() 40 - }) 41 - }) 42 - 43 - const data = await response.json() 44 - 45 - if (!response.ok) { 46 - error = data.error_description || data.error || $_('oauth.twoFactorCode.errors.verificationFailed') 47 - submitting = false 48 - return 49 - } 50 - 51 - if (data.redirect_uri) { 52 - window.location.href = data.redirect_uri 53 - return 54 - } 55 - 56 - error = $_('oauth.twoFactorCode.errors.unexpectedResponse') 57 - submitting = false 58 - } catch { 59 - error = $_('oauth.twoFactorCode.errors.connectionFailed') 60 - submitting = false 61 - } 62 - } 63 - 64 - function handleCancel() { 65 - const requestUri = getRequestUri() 66 - if (requestUri) { 67 - navigate(routes.oauthLogin, { params: { request_uri: requestUri } }) 68 - } else { 69 - window.history.back() 70 - } 71 - } 72 - 73 - let channel = $derived(getChannel()) 74 - </script> 75 - 76 - <div class="oauth-2fa-container"> 77 - <h1>{$_('oauth.twoFactorCode.title')}</h1> 78 - <p class="subtitle"> 79 - {$_('oauth.twoFactorCode.subtitle', { values: { channel } })} 80 - </p> 81 - 82 - {#if error} 83 - <div class="error">{error}</div> 84 - {/if} 85 - 86 - <form onsubmit={handleSubmit}> 87 - <div> 88 - <label for="code">{$_('oauth.twoFactorCode.codeLabel')}</label> 89 - <input 90 - id="code" 91 - type="text" 92 - bind:value={code} 93 - placeholder={$_('oauth.twoFactorCode.codePlaceholder')} 94 - disabled={submitting} 95 - required 96 - maxlength="6" 97 - pattern="[0-9]{6}" 98 - autocomplete="one-time-code" 99 - inputmode="numeric" 100 - /> 101 - </div> 102 - 103 - <div class="actions"> 104 - <button type="button" class="cancel" onclick={handleCancel} disabled={submitting}> 105 - {$_('common.cancel')} 106 - </button> 107 - <button type="submit" disabled={submitting || code.trim().length !== 6}> 108 - {submitting ? $_('common.verifying') : $_('common.verify')} 109 - </button> 110 - </div> 111 - </form> 112 - </div>
-121
frontend/src/routes/OAuthTotp.svelte
··· 1 - <script lang="ts"> 2 - import { navigate, routes } from '../lib/router.svelte' 3 - import { _ } from '../lib/i18n' 4 - 5 - let code = $state('') 6 - let trustDevice = $state(false) 7 - let submitting = $state(false) 8 - let error = $state<string | null>(null) 9 - 10 - function getRequestUri(): string | null { 11 - const params = new URLSearchParams(window.location.search) 12 - return params.get('request_uri') 13 - } 14 - 15 - async function handleSubmit(e: Event) { 16 - e.preventDefault() 17 - const requestUri = getRequestUri() 18 - if (!requestUri) { 19 - error = $_('common.error') 20 - return 21 - } 22 - 23 - submitting = true 24 - error = null 25 - 26 - try { 27 - const response = await fetch('/oauth/authorize/2fa', { 28 - method: 'POST', 29 - headers: { 30 - 'Content-Type': 'application/json', 31 - 'Accept': 'application/json' 32 - }, 33 - body: JSON.stringify({ 34 - request_uri: requestUri, 35 - code: code.trim().toUpperCase(), 36 - trust_device: trustDevice 37 - }) 38 - }) 39 - 40 - const data = await response.json() 41 - 42 - if (!response.ok) { 43 - error = data.error_description || data.error || $_('common.error') 44 - submitting = false 45 - return 46 - } 47 - 48 - if (data.redirect_uri) { 49 - window.location.href = data.redirect_uri 50 - return 51 - } 52 - 53 - error = $_('common.error') 54 - submitting = false 55 - } catch { 56 - error = $_('common.error') 57 - submitting = false 58 - } 59 - } 60 - 61 - function handleCancel() { 62 - const requestUri = getRequestUri() 63 - if (requestUri) { 64 - navigate(routes.oauthLogin, { params: { request_uri: requestUri } }) 65 - } else { 66 - window.history.back() 67 - } 68 - } 69 - 70 - let isBackupCode = $derived(code.trim().length === 8 && /^[A-Z0-9]+$/i.test(code.trim())) 71 - let isTotpCode = $derived(code.trim().length === 6 && /^[0-9]+$/.test(code.trim())) 72 - let canSubmit = $derived(isBackupCode || isTotpCode) 73 - </script> 74 - 75 - <div class="oauth-totp-container"> 76 - <h1>{$_('oauth.totp.title')}</h1> 77 - 78 - {#if error} 79 - <div class="error">{error}</div> 80 - {/if} 81 - 82 - <form onsubmit={handleSubmit}> 83 - <div> 84 - <label for="code">{$_('oauth.totp.codePlaceholder')}</label> 85 - <input 86 - id="code" 87 - type="text" 88 - bind:value={code} 89 - placeholder={isBackupCode ? $_('oauth.totp.backupCodePlaceholder') : $_('oauth.totp.codePlaceholder')} 90 - disabled={submitting} 91 - required 92 - maxlength="8" 93 - autocomplete="one-time-code" 94 - autocapitalize="characters" 95 - /> 96 - {#if isBackupCode || isTotpCode} 97 - <p class="hint"> 98 - {isBackupCode ? $_('oauth.totp.hintBackupCode') : $_('oauth.totp.hintTotpCode')} 99 - </p> 100 - {/if} 101 - </div> 102 - 103 - <label class="trust-device-label"> 104 - <input 105 - type="checkbox" 106 - bind:checked={trustDevice} 107 - disabled={submitting} 108 - /> 109 - <span>{$_('oauth.totp.trustDevice')}</span> 110 - </label> 111 - 112 - <div class="actions"> 113 - <button type="button" class="cancel" onclick={handleCancel} disabled={submitting}> 114 - {$_('common.cancel')} 115 - </button> 116 - <button type="submit" disabled={submitting || !canSubmit}> 117 - {submitting ? $_('common.verifying') : $_('common.verify')} 118 - </button> 119 - </div> 120 - </form> 121 - </div>
-27
frontend/src/routes/RegisterPasskey.svelte
··· 1 - <script lang="ts"> 2 - import { startOAuthRegister } from '../lib/oauth' 3 - import { _ } from '../lib/i18n' 4 - 5 - let error = $state<string | null>(null) 6 - let initiated = false 7 - 8 - $effect(() => { 9 - if (!initiated) { 10 - initiated = true 11 - startOAuthRegister().catch((err) => { 12 - error = err instanceof Error ? err.message : 'Failed to start registration' 13 - }) 14 - } 15 - }) 16 - </script> 17 - 18 - <div class="register-redirect"> 19 - {#if error} 20 - <div class="message error">{error}</div> 21 - <a href="/app/login">{$_('register.signIn')}</a> 22 - {:else} 23 - <div class="loading-content"> 24 - <p>{$_('common.loading')}</p> 25 - </div> 26 - {/if} 27 - </div>

History

3 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
refactor(frontend): simplify API client and delete obsolete auth routes
expand 0 comments
pull request successfully merged
1 commit
expand
refactor(frontend): simplify API client and delete obsolete auth routes
expand 0 comments
1 commit
expand
refactor(frontend): simplify API client and delete obsolete auth routes
expand 0 comments