-
Notifications
You must be signed in to change notification settings - Fork 67
Feat/sdk v0.2.0 upgrade #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
253c2f4
e137716
2b916ab
c4217f5
1730e40
5fb59f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { UrBackendClient } from '../client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AuthUser, AuthResponse, SignUpPayload, LoginPayload } from '../types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AuthUser, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AuthResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SignUpPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LoginPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UpdateProfilePayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ChangePasswordPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| VerifyEmailPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResendOtpPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RequestPasswordResetPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResetPasswordPayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SocialExchangePayload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SocialExchangeResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ApiResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AuthError } from '../errors'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class AuthModule { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -21,7 +35,15 @@ export class AuthModule { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await this.client.request<AuthResponse>('POST', '/api/userAuth/login', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.sessionToken = response.token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.sessionToken = response.accessToken || response.token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.accessToken && response.token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'urbackend-sdk: The server returned "token" which is deprecated. Please update your backend to return "accessToken".', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,16 +54,157 @@ export class AuthModule { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeToken = token || this.sessionToken; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!activeToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new AuthError('Authentication token is required for /me endpoint', 401, '/api/userAuth/me'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new AuthError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Authentication token is required for /me endpoint', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 401, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/api/userAuth/me', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<AuthUser>('GET', '/api/userAuth/me', { token: activeToken }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Clear the local session token | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Update the current authenticated user's profile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async updateProfile(payload: UpdateProfilePayload, token?: string): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeToken = token || this.sessionToken; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!activeToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new AuthError('Authentication token is required to update profile', 401, '/api/userAuth/update-profile'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('PUT', '/api/userAuth/update-profile', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token: activeToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Change the current authenticated user's password | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async changePassword(payload: ChangePasswordPayload, token?: string): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeToken = token || this.sessionToken; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!activeToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new AuthError('Authentication token is required to change password', 401, '/api/userAuth/change-password'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('PUT', '/api/userAuth/change-password', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token: activeToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Verify user email with OTP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async verifyEmail(payload: VerifyEmailPayload): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('POST', '/api/userAuth/verify-email', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Resend verification OTP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async resendVerificationOtp(payload: ResendOtpPayload): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('POST', '/api/userAuth/resend-verification-otp', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Request password reset OTP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async requestPasswordReset(payload: RequestPasswordResetPayload): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('POST', '/api/userAuth/request-password-reset', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Reset user password with OTP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public logout(): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async resetPassword(payload: ResetPasswordPayload): Promise<{ message: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<{ message: string }>('POST', '/api/userAuth/reset-password', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: payload, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Get public-safe profile by username | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async publicProfile(username: string): Promise<AuthUser> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.client.request<AuthUser>('GET', `/api/userAuth/public/${username}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Refresh the access token | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param refreshToken Optional refresh token for header mode. If omitted, uses cookie mode. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async refreshToken(refreshToken?: string): Promise<AuthResponse> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const options: any = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (refreshToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options.headers = { 'x-refresh-token': refreshToken, 'x-refresh-token-mode': 'header' }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options.credentials = 'include'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await this.client.request<AuthResponse>('POST', '/api/userAuth/refresh-token', options); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+142
to
+150
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.sessionToken = response.accessToken || response.token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Returns the start URL for social authentication. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Redirect the user's browser to this URL to begin the flow. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public socialStart(provider: 'github' | 'google'): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return `${this.client['baseUrl']}/api/userAuth/social/${provider}/start?key=${this.client['apiKey']}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | |
| * Returns the start URL for social authentication. | |
| * Redirect the user's browser to this URL to begin the flow. | |
| */ | |
| public socialStart(provider: 'github' | 'google'): string { | |
| return `${this.client['baseUrl']}/api/userAuth/social/${provider}/start?key=${this.client['apiKey']}`; | |
| private getClientBaseUrl(): string { | |
| const clientWithGetters = this.client as UrBackendClient & { | |
| getBaseUrl?: () => string; | |
| }; | |
| if (typeof clientWithGetters.getBaseUrl !== 'function') { | |
| throw new Error('UrBackendClient must expose a public getBaseUrl() method for social auth navigation.'); | |
| } | |
| return clientWithGetters.getBaseUrl(); | |
| } | |
| private getClientApiKey(): string { | |
| const clientWithGetters = this.client as UrBackendClient & { | |
| getApiKey?: () => string; | |
| }; | |
| if (typeof clientWithGetters.getApiKey !== 'function') { | |
| throw new Error('UrBackendClient must expose a public getApiKey() method for social auth navigation.'); | |
| } | |
| return clientWithGetters.getApiKey(); | |
| } | |
| /** | |
| * Returns the start URL for social authentication. | |
| * Redirect the user's browser to this URL to begin the flow. | |
| */ | |
| public socialStart(provider: 'github' | 'google'): string { | |
| return `${this.getClientBaseUrl()}/api/userAuth/social/${provider}/start?key=${this.getClientApiKey()}`; |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
socialExchange() is typed to return ApiResponse<SocialExchangeResponse>, but UrBackendClient.request() unwraps { success, data, message } responses by returning json.data when present. Since the public API actually returns { success: true, data: { refreshToken }, ... } for this endpoint, this method will resolve to { refreshToken: ... } (data only), not an ApiResponse. Update the return type/implementation to return SocialExchangeResponse (or add a request option to disable unwrapping and return the full envelope) so consumers don't break when they try to access .data.refreshToken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client.request()currently uses(options as any).headersto merge custom headers. OnceRequestOptions.headersexists, remove theanycast and use the typed property; this prevents silently accepting non-string header values and improves type safety across modules.