Browse Source

feat(auth): add support for access token login configuration and handling

pull/5912/head
Germán Martín 2 months ago
parent
commit
421bc3d040
  1. 4
      .env.example
  2. 7
      apps/api/src/app/info/info.service.ts
  3. 11
      apps/api/src/app/user/user.controller.ts
  4. 1
      apps/api/src/services/configuration/configuration.service.ts
  5. 1
      apps/api/src/services/interfaces/environment.interface.ts
  6. 1
      apps/client/src/app/components/header/header.component.ts
  7. 1
      apps/client/src/app/components/login-with-access-token-dialog/interfaces/interfaces.ts
  8. 68
      apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html
  9. 1
      libs/common/src/lib/interfaces/info-item.interface.ts

4
.env.example

@ -20,6 +20,10 @@ ROOT_URL=https://<your_domain>
# Enable social login (Google, OIDC, etc.)
# ENABLE_FEATURE_SOCIAL_LOGIN=true
# Enable access token login (anonymous login)
# Set to false to disable login by access token when using OAuth providers
ENABLE_ACCESS_TOKEN_LOGIN=true
# OIDC AUTHENTICATION (Optional)
# Enable/disable OIDC authentication
OIDC_ENABLED=false

7
apps/api/src/app/info/info.service.ts

@ -128,7 +128,11 @@ export class InfoService {
this.subscriptionService.getSubscriptionOffer({ key: 'default' })
]);
if (isUserSignupEnabled) {
const isAccessTokenLoginEnabled = this.configurationService.get(
'ENABLE_ACCESS_TOKEN_LOGIN'
);
if (isUserSignupEnabled && isAccessTokenLoginEnabled) {
globalPermissions.push(permissions.createUserAccount);
}
@ -137,6 +141,7 @@ export class InfoService {
benchmarks,
demoAuthToken,
globalPermissions,
isAccessTokenLoginEnabled,
isReadOnlyMode,
platforms,
statistics,

11
apps/api/src/app/user/user.controller.ts

@ -126,6 +126,17 @@ export class UserController {
);
}
const isAccessTokenLoginEnabled = this.configurationService.get(
'ENABLE_ACCESS_TOKEN_LOGIN'
);
if (!isAccessTokenLoginEnabled) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
const hasAdmin = await this.userService.hasAdmin();
const { accessToken, id, role } = await this.userService.createUser({

1
apps/api/src/services/configuration/configuration.service.ts

@ -40,6 +40,7 @@ export class ConfigurationService {
DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER: json({
default: []
}),
ENABLE_ACCESS_TOKEN_LOGIN: bool({ default: true }),
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: bool({ default: false }),
ENABLE_FEATURE_READ_ONLY_MODE: bool({ default: false }),
ENABLE_FEATURE_SOCIAL_LOGIN: bool({ default: false }),

1
apps/api/src/services/interfaces/environment.interface.ts

@ -16,6 +16,7 @@ export interface Environment extends CleanedEnvAccessors {
DATA_SOURCE_IMPORT: string;
DATA_SOURCES: string[];
DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER: string[];
ENABLE_ACCESS_TOKEN_LOGIN: boolean;
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: boolean;
ENABLE_FEATURE_READ_ONLY_MODE: boolean;
ENABLE_FEATURE_SOCIAL_LOGIN: boolean;

1
apps/client/src/app/components/header/header.component.ts

@ -280,6 +280,7 @@ export class GfHeaderComponent implements OnChanges {
data: {
accessToken: '',
hasPermissionToUseSocialLogin: this.hasPermissionForSocialLogin,
isAccessTokenLoginEnabled: this.info?.isAccessTokenLoginEnabled,
socialLoginProviders: this.info?.socialLoginProviders,
title: $localize`Sign in`
},

1
apps/client/src/app/components/login-with-access-token-dialog/interfaces/interfaces.ts

@ -1,6 +1,7 @@
export interface LoginWithAccessTokenDialogParams {
accessToken: string;
hasPermissionToUseSocialLogin: boolean;
isAccessTokenLoginEnabled?: boolean;
socialLoginProviders?: string[];
title: string;
}

68
apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html

@ -3,28 +3,32 @@
<div class="py-3" mat-dialog-content>
<div class="align-items-center d-flex flex-column">
<form class="w-100">
<mat-form-field appearance="outline" class="without-hint w-100">
<mat-label i18n>Security Token</mat-label>
<input
matInput
[formControl]="accessTokenFormControl"
[type]="isAccessTokenHidden ? 'password' : 'text'"
(keydown.enter)="onLoginWithAccessToken(); $event.preventDefault()"
/>
<button
mat-button
matSuffix
type="button"
(click)="isAccessTokenHidden = !isAccessTokenHidden"
>
<ion-icon
[name]="isAccessTokenHidden ? 'eye-outline' : 'eye-off-outline'"
@if (data.isAccessTokenLoginEnabled !== false) {
<mat-form-field appearance="outline" class="without-hint w-100">
<mat-label i18n>Security Token</mat-label>
<input
matInput
[formControl]="accessTokenFormControl"
[type]="isAccessTokenHidden ? 'password' : 'text'"
(keydown.enter)="onLoginWithAccessToken(); $event.preventDefault()"
/>
</button>
</mat-form-field>
<button
mat-button
matSuffix
type="button"
(click)="isAccessTokenHidden = !isAccessTokenHidden"
>
<ion-icon
[name]="isAccessTokenHidden ? 'eye-outline' : 'eye-off-outline'"
/>
</button>
</mat-form-field>
}
@if (data.hasPermissionToUseSocialLogin) {
<div class="my-3 text-center text-muted" i18n>or</div>
@if (data.isAccessTokenLoginEnabled !== false) {
<div class="my-3 text-center text-muted" i18n>or</div>
}
<div class="d-flex flex-column gap-2">
@if (data.socialLoginProviders?.includes('google')) {
<a
@ -59,16 +63,18 @@
>Stay signed in</mat-checkbox
>
</div>
<div>
<button
color="primary"
mat-flat-button
[disabled]="
!(accessTokenFormControl.dirty && accessTokenFormControl.valid)
"
(click)="onLoginWithAccessToken()"
>
<ng-container i18n>Sign in</ng-container>
</button>
</div>
@if (data.isAccessTokenLoginEnabled !== false) {
<div>
<button
color="primary"
mat-flat-button
[disabled]="
!(accessTokenFormControl.dirty && accessTokenFormControl.valid)
"
(click)="onLoginWithAccessToken()"
>
<ng-container i18n>Sign in</ng-container>
</button>
</div>
}
</div>

1
libs/common/src/lib/interfaces/info-item.interface.ts

@ -11,6 +11,7 @@ export interface InfoItem {
demoAuthToken: string;
fearAndGreedDataSource?: string;
globalPermissions: string[];
isAccessTokenLoginEnabled?: boolean;
isDataGatheringEnabled?: string;
isReadOnlyMode?: boolean;
platforms: Platform[];

Loading…
Cancel
Save