-
Notifications
You must be signed in to change notification settings - Fork 1
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
It is correct when decode token ? #137
Changes from all commits
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 | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,6 +6,9 @@ import { | |||||||||||||||||||||||||||||||||||||||||||
import {SubscriptionAwareComponent} from '../subscription-aware.component'; | ||||||||||||||||||||||||||||||||||||||||||||
import {BehaviorSubject, Observable, filter, map, switchMap} from 'rxjs'; | ||||||||||||||||||||||||||||||||||||||||||||
import {JwtPayload} from 'jwt-decode'; | ||||||||||||||||||||||||||||||||||||||||||||
import {JwtHelperService} from '@auth0/angular-jwt'; | ||||||||||||||||||||||||||||||||||||||||||||
import {UserRole} from '../../authorization/enums/role-names'; | ||||||||||||||||||||||||||||||||||||||||||||
import {UUID} from '../../../../types/uuid'; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
interface UserInfoEmailScope { | ||||||||||||||||||||||||||||||||||||||||||||
email: string; | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -50,7 +53,7 @@ export class ApplicationService | |||||||||||||||||||||||||||||||||||||||||||
implements OnDestroy | ||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||
public readonly userInfoSubject; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
jwtHelperService = new JwtHelperService(); | ||||||||||||||||||||||||||||||||||||||||||||
constructor(private readonly oidcSecurityService: OidcSecurityService) { | ||||||||||||||||||||||||||||||||||||||||||||
super(); | ||||||||||||||||||||||||||||||||||||||||||||
this.userInfoSubject = new BehaviorSubject<UserInfoData | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -79,6 +82,7 @@ export class ApplicationService | |||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
login(): void { | ||||||||||||||||||||||||||||||||||||||||||||
this.oidcSecurityService.authorize(); | ||||||||||||||||||||||||||||||||||||||||||||
this.getAccessToken(); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
isMobile(): boolean { | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -97,9 +101,46 @@ export class ApplicationService | |||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
getStoredAccessToken(): string | null { | ||||||||||||||||||||||||||||||||||||||||||||
return localStorage.getItem('access_token') ?? ''; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
getUserId(): UUID | null { | ||||||||||||||||||||||||||||||||||||||||||||
const token = this.getStoredAccessToken(); | ||||||||||||||||||||||||||||||||||||||||||||
if (!token) { | ||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
const userObject = this.jwtHelperService.decodeToken(token); | ||||||||||||||||||||||||||||||||||||||||||||
return 'userId' in userObject ? userObject.userId : ''; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+107
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add token validation before decoding. The token is decoded without validating its integrity or expiration. Add validation before using the token: getUserId(): UUID | null {
const token = this.getStoredAccessToken();
- if (!token) {
+ if (!token || this.isTokenExpired()) {
return null;
}
+ try {
const userObject = this.jwtHelperService.decodeToken(token);
return 'userId' in userObject ? userObject.userId : '';
+ } catch (error) {
+ console.error('Invalid token:', error);
+ return null;
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
getUserRole(): UserRole { | ||||||||||||||||||||||||||||||||||||||||||||
const token = this.getStoredAccessToken(); | ||||||||||||||||||||||||||||||||||||||||||||
if (!token) { | ||||||||||||||||||||||||||||||||||||||||||||
return UserRole.ENTERPRISE_OWNER; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
const userObject = this.jwtHelperService.decodeToken(token); | ||||||||||||||||||||||||||||||||||||||||||||
return 'role' in userObject ? userObject.role : ''; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+116
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid default role assignment. Defaulting to ENTERPRISE_OWNER when token is missing could lead to privilege escalation. Modify to use a safer default: getUserRole(): UserRole {
const token = this.getStoredAccessToken();
if (!token) {
- return UserRole.ENTERPRISE_OWNER;
+ return UserRole.GUEST;
}
+ try {
const userObject = this.jwtHelperService.decodeToken(token);
return 'role' in userObject ? userObject.role : '';
+ } catch (error) {
+ console.error('Invalid token:', error);
+ return UserRole.GUEST;
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
private postLogout(): void { | ||||||||||||||||||||||||||||||||||||||||||||
sessionStorage.clear(); | ||||||||||||||||||||||||||||||||||||||||||||
localStorage.clear(); | ||||||||||||||||||||||||||||||||||||||||||||
// localStorage.removeItem('access_token'); | ||||||||||||||||||||||||||||||||||||||||||||
this.userInfoSubject.next(null); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
private getAccessToken(): void { | ||||||||||||||||||||||||||||||||||||||||||||
this.oidcSecurityService.getAccessToken().subscribe((token: string) => { | ||||||||||||||||||||||||||||||||||||||||||||
if (token) { | ||||||||||||||||||||||||||||||||||||||||||||
localStorage.setItem('access_token', token); | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is correct way to store access_token. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. never manually put secret to localStorage, we need to handle access token in application lifecycle instead |
||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
private isTokenExpired(): boolean { | ||||||||||||||||||||||||||||||||||||||||||||
if (this.getStoredAccessToken() == null) { | ||||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
return this.jwtHelperService.isTokenExpired(this.getStoredAccessToken()); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,8 @@ | |
<img | ||
ngSrc="assets/images/payment-method/momo.svg" | ||
alt="Logo" | ||
width="20" | ||
height="20" | ||
width="30" | ||
height="30" | ||
priority | ||
/> | ||
<img | ||
|
@@ -21,27 +21,31 @@ | |
width="30" | ||
height="30" | ||
priority | ||
class="border" | ||
/> | ||
<img | ||
ngSrc="assets/images/payment-method/napas.svg" | ||
alt="Logo" | ||
width="30" | ||
height="30" | ||
priority | ||
class="border" | ||
/> | ||
<img | ||
ngSrc="assets/images/payment-method/visa.svg" | ||
alt="Logo" | ||
width="30" | ||
height="30" | ||
priority | ||
class="border" | ||
/> | ||
<img | ||
ngSrc="assets/images/payment-method/master-card.svg" | ||
alt="Logo" | ||
width="30" | ||
height="30" | ||
priority | ||
class="border" | ||
/> | ||
</div> | ||
</div> | ||
|
@@ -68,12 +72,12 @@ | |
> | ||
<div class="flex flex-col gap-16"> | ||
<div | ||
class="bg-white rounded-md border-[#E5E7EB] border px-4 py-2 mt-8 mx-6 flex justify-center items-center lg:justify-start" | ||
class="bg-white rounded-md border-[#E5E7EB] border px-4 py-2 mt-8 mx-6 flex justify-center items-center lg:justify-start text-black" | ||
> | ||
<p>{{ "purchaseCredit.selectCreditPackage" | translate }}</p> | ||
</div> | ||
<div | ||
class="flex lg:flex-row flex-col gap-16 items-center justify-center mx-6" | ||
class="flex lg:flex-row flex-col gap-16 items-center justify-center mx-6 text-black" | ||
> | ||
@for (package of creditPackages; track package.id) { | ||
<card-template | ||
|
@@ -126,7 +130,7 @@ | |
<p-accordion [value]="0"> | ||
@for (tab of tabs; track tab.title) { | ||
<p-accordion-panel [value]="tab.value"> | ||
<p-accordion-header class="bg-[#F4FCFD]">{{ | ||
<p-accordion-header class="bg-[#F4FCFD] text-black">{{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. although UX/UI isn't the most important to put more effort, but hard code color like #F4FCFD is very hard to maintain later, in addition, use |
||
tab.title | translate | ||
}}</p-accordion-header> | ||
<p-accordion-content> | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -56,6 +56,8 @@ public Map<String, Object> getCustomClaimsForJwtAuthenticationToken(String email | |||||||||||||||||
.map(UserEnterpriseEntity::getEnterprise) | ||||||||||||||||||
.map(UUID::toString) | ||||||||||||||||||
.orElse(null)); | ||||||||||||||||||
claims.put("userId", user.getId()); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. userId need to config as |
||||||||||||||||||
claims.put("role", user.getEnterprise().getRole()); | ||||||||||||||||||
Comment on lines
+59
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate and sanitize JWT claims. The new claims (
Add validation and consider using a more secure approach: - claims.put("userId", user.getId());
- claims.put("role", user.getEnterprise().getRole());
+ claims.put("sub", user.getId().toString()); // Use standard JWT claim
+ if (user.getEnterprise() != null && user.getEnterprise().getRole() != null) {
+ claims.put("role", user.getEnterprise().getRole().name());
+ } else {
+ claims.put("role", "GUEST");
+ } 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||
claims.put("permissions", buildingPermissions); | ||||||||||||||||||
return claims; | ||||||||||||||||||
} | ||||||||||||||||||
|
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.
Use secure storage for access tokens.
Storing tokens in localStorage makes them vulnerable to XSS attacks.
Consider using more secure alternatives: