Skip to content

Commit f02356a

Browse files
Phase 1 - Add AWS Amplify Authentication and User Models
- Integrate AWS Amplify authentication dependencies - Extend auth and user models to support Cognito user attributes - Add types for social authentication, auth state, and Cognito user details - Update package dependencies to include Amplify authentication modules
1 parent d783b4c commit f02356a

File tree

8 files changed

+6836
-2616
lines changed

8 files changed

+6836
-2616
lines changed

frontend/package-lock.json

Lines changed: 6362 additions & 2616 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
]
3030
},
3131
"dependencies": {
32+
"@aws-amplify/auth": "^6.0.15",
33+
"@aws-amplify/core": "^6.0.15",
34+
"@aws-amplify/ui-react": "^6.1.1",
3235
"@capacitor/android": "6.2.0",
3336
"@capacitor/app": "6.0.2",
3437
"@capacitor/assets": "3.0.5",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* AWS Configuration for the application
3+
*
4+
* This file contains configuration settings for AWS services including Cognito User Pool.
5+
* In a production environment, these values should come from environment variables.
6+
*/
7+
8+
export const REGION = 'us-east-1'; // Replace with your AWS region
9+
10+
/**
11+
* Cognito Configuration
12+
*/
13+
export const COGNITO_CONFIG = {
14+
// User Pool
15+
USER_POOL_ID: import.meta.env.VITE_COGNITO_USER_POOL_ID || 'us-east-1_xxxxxxxx', // Replace with your User Pool ID
16+
USER_POOL_WEB_CLIENT_ID: import.meta.env.VITE_COGNITO_APP_CLIENT_ID || 'xxxxxxxxxxxxxxxxxxxxxxxxxx', // Replace with your App Client ID
17+
18+
// OAuth Configuration (for Social Login)
19+
OAUTH: {
20+
domain: import.meta.env.VITE_COGNITO_DOMAIN || 'your-domain.auth.us-east-1.amazoncognito.com', // Replace with your Cognito domain
21+
scope: ['email', 'profile', 'openid'],
22+
redirectSignIn: import.meta.env.VITE_REDIRECT_SIGN_IN || 'http://localhost:3000/',
23+
redirectSignOut: import.meta.env.VITE_REDIRECT_SIGN_OUT || 'http://localhost:3000/',
24+
responseType: 'code' as const
25+
},
26+
27+
// Auth mechanisms
28+
AUTH_MECHANISMS: ['EMAIL'],
29+
30+
// Social providers
31+
SOCIAL_PROVIDERS: ['Google', 'SignInWithApple'],
32+
};
33+
34+
/**
35+
* Amplify Configuration object for initializing Amplify (V6 format)
36+
*/
37+
export const amplifyConfig = {
38+
Auth: {
39+
Cognito: {
40+
userPoolId: COGNITO_CONFIG.USER_POOL_ID,
41+
userPoolClientId: COGNITO_CONFIG.USER_POOL_WEB_CLIENT_ID,
42+
loginWith: {
43+
email: true,
44+
phone: false,
45+
username: false,
46+
oauth: {
47+
domain: COGNITO_CONFIG.OAUTH.domain,
48+
scopes: COGNITO_CONFIG.OAUTH.scope,
49+
redirectSignIn: [COGNITO_CONFIG.OAUTH.redirectSignIn],
50+
redirectSignOut: [COGNITO_CONFIG.OAUTH.redirectSignOut],
51+
responseType: 'code' as const
52+
}
53+
}
54+
}
55+
},
56+
// Configure only the region you need to use
57+
region: REGION
58+
};

frontend/src/common/models/auth.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,54 @@ export type UserTokens = {
1616
export type RememberMe = {
1717
username: string;
1818
};
19+
20+
/**
21+
* The `CognitoUserAttributes` type. Cognito user attributes.
22+
*/
23+
export type CognitoUserAttributes = {
24+
email: string;
25+
email_verified?: boolean;
26+
given_name?: string;
27+
family_name?: string;
28+
phone_number?: string;
29+
phone_number_verified?: boolean;
30+
sub?: string;
31+
};
32+
33+
/**
34+
* The `AuthError` type. Authentication error details.
35+
*/
36+
export type AuthError = {
37+
code: string;
38+
message: string;
39+
name: string;
40+
};
41+
42+
/**
43+
* The `SignUpParams` type. Parameters for user registration.
44+
*/
45+
export type SignUpParams = {
46+
username: string;
47+
password: string;
48+
attributes: {
49+
email: string;
50+
given_name: string;
51+
family_name: string;
52+
};
53+
};
54+
55+
/**
56+
* Social authentication providers
57+
*/
58+
export enum SocialAuthProvider {
59+
GOOGLE = 'Google',
60+
APPLE = 'SignInWithApple',
61+
}
62+
63+
/**
64+
* The `AuthState` type. Current auth state.
65+
*/
66+
export enum AuthState {
67+
SIGNED_IN = 'signedIn',
68+
SIGNED_OUT = 'signedOut',
69+
}

frontend/src/common/models/user.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,33 @@ export type User = {
3434
address: Address;
3535
company: Company;
3636
};
37+
38+
/**
39+
* Cognito User type aligned with AWS Cognito attributes
40+
*/
41+
export type CognitoUser = {
42+
// Unique identifier (sub from Cognito)
43+
id: string;
44+
// Username (usually email in our case)
45+
username: string;
46+
// Email address
47+
email: string;
48+
// Full name composed of given_name and family_name
49+
name?: string;
50+
// First name from Cognito given_name attribute
51+
firstName?: string;
52+
// Last name from Cognito family_name attribute
53+
lastName?: string;
54+
// Phone number
55+
phone?: string;
56+
// Whether email is verified
57+
emailVerified?: boolean;
58+
// Whether phone is verified
59+
phoneVerified?: boolean;
60+
// User groups/roles
61+
groups?: string[];
62+
// Created date
63+
createdAt?: string;
64+
// Updated date
65+
updatedAt?: string;
66+
};
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { signIn, signUp, confirmSignUp, signOut,
2+
fetchAuthSession, getCurrentUser } from '@aws-amplify/auth';
3+
import { Amplify } from 'aws-amplify';
4+
import { amplifyConfig } from '../../config/aws-config';
5+
import { UserTokens } from '../../models/auth';
6+
7+
/**
8+
* Initialize AWS Amplify with the configuration
9+
*/
10+
Amplify.configure(amplifyConfig);
11+
12+
/**
13+
* Cognito Authentication Service
14+
*
15+
* A service to handle interactions with AWS Cognito for authentication and user management
16+
*/
17+
export class CognitoAuthService {
18+
19+
/**
20+
* Sign in with email and password
21+
* @param username Email address
22+
* @param password Password
23+
* @returns Promise resolving to the authenticated user
24+
*/
25+
static async signIn(username: string, password: string) {
26+
try {
27+
const user = await signIn({ username, password });
28+
return user;
29+
} catch (error) {
30+
this.handleAuthError(error);
31+
throw error;
32+
}
33+
}
34+
35+
/**
36+
* Sign up a new user
37+
* @param email User's email
38+
* @param password User's password
39+
* @param attributes User attributes (first name, last name)
40+
* @returns Promise resolving to the sign up result
41+
*/
42+
static async signUp(email: string, password: string, attributes: { firstName: string; lastName: string }) {
43+
try {
44+
const result = await signUp({
45+
username: email,
46+
password,
47+
options: {
48+
userAttributes: {
49+
email,
50+
given_name: attributes.firstName,
51+
family_name: attributes.lastName,
52+
}
53+
}
54+
});
55+
return result;
56+
} catch (error) {
57+
this.handleAuthError(error);
58+
throw error;
59+
}
60+
}
61+
62+
/**
63+
* Confirm sign up with verification code
64+
* @param username Email address
65+
* @param code Verification code
66+
* @returns Promise resolving to the confirmation result
67+
*/
68+
static async confirmSignUp(username: string, code: string) {
69+
try {
70+
return await confirmSignUp({ username, confirmationCode: code });
71+
} catch (error) {
72+
this.handleAuthError(error);
73+
throw error;
74+
}
75+
}
76+
77+
/**
78+
* Sign out the current user
79+
* @returns Promise resolving when sign out is complete
80+
*/
81+
static async signOut() {
82+
try {
83+
return await signOut();
84+
} catch (error) {
85+
this.handleAuthError(error);
86+
throw error;
87+
}
88+
}
89+
90+
/**
91+
* Get current authenticated user
92+
* @returns Promise resolving to the current authenticated user
93+
*/
94+
static async getCurrentUser() {
95+
try {
96+
return await getCurrentUser();
97+
} catch (_error) {
98+
// Not throwing here as this is often used to check if a user is signed in
99+
console.error('Error getting current user:', _error);
100+
return null;
101+
}
102+
}
103+
104+
/**
105+
* Get current session
106+
* @returns Promise resolving to the current session
107+
*/
108+
static async getCurrentSession() {
109+
try {
110+
return await fetchAuthSession();
111+
} catch (_error) {
112+
console.error('Error getting current session:', _error);
113+
return null;
114+
}
115+
}
116+
117+
/**
118+
* Get user tokens from the current session
119+
* @returns Promise resolving to UserTokens or null if no session
120+
*/
121+
static async getUserTokens(): Promise<UserTokens | null> {
122+
try {
123+
const session = await fetchAuthSession();
124+
125+
if (!session.tokens || !session.tokens.accessToken || !session.tokens.idToken) {
126+
return null;
127+
}
128+
129+
// Get expiration time from access token
130+
const accessToken = session.tokens.accessToken;
131+
const expirationTime = accessToken.payload.exp ?? 0;
132+
133+
return {
134+
access_token: accessToken.toString(),
135+
id_token: session.tokens.idToken.toString(),
136+
refresh_token: '', // AWS Amplify v6 doesn't expose refresh token directly
137+
token_type: 'bearer',
138+
expires_in: Math.floor((new Date(expirationTime * 1000).getTime() - Date.now()) / 1000),
139+
expires_at: new Date(expirationTime * 1000).toISOString(),
140+
};
141+
} catch (_error) {
142+
this.handleAuthError(_error);
143+
return null;
144+
}
145+
}
146+
147+
/**
148+
* Initiate social sign in (Google or Apple)
149+
* @param provider 'Google' or 'SignInWithApple'
150+
* @returns Promise
151+
*/
152+
static async federatedSignIn(provider: 'Google' | 'SignInWithApple') {
153+
try {
154+
// This needs OAuth configuration in the Amplify setup
155+
// In AWS Amplify v6, we'd use a different approach for federated sign in
156+
// Placeholder for now
157+
console.warn('federatedSignIn is not implemented in this version', provider);
158+
// In production, federated sign-in would be handled differently
159+
// For example, using a hosted UI or custom implementation
160+
return null;
161+
} catch (error) {
162+
this.handleAuthError(error);
163+
throw error;
164+
}
165+
}
166+
167+
/**
168+
* Handle common authentication errors
169+
* @param error The error from Cognito
170+
*/
171+
private static handleAuthError(error: unknown) {
172+
// Log the error for debugging
173+
console.error('Authentication error:', error);
174+
175+
// You can add custom error handling logic here
176+
// For example, mapping Cognito error codes to user-friendly messages
177+
}
178+
}
179+
180+
/**
181+
* Export singleton instance for easy imports
182+
*/
183+
export default CognitoAuthService;

0 commit comments

Comments
 (0)