mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 00:16:41 +02:00
fix(ms365): OAuth-Connect via API-Call statt direktem Browser-Link
Problem: <a href="/api/v1/auth/integrations/microsoft-365"> sendet keinen
JWT-Authorization-Header (JWT liegt im Memory, nicht als Cookie).
Lösung:
- Backend: initM365Integration gibt JSON {url} zurück statt server-redirect
- Frontend: integrationsApi.connectM365() ruft Endpoint via Axios ab, dann
window.location.href zur OAuth-URL
- ProfilePage + EmailsTab + CalendarTab + TasksTab: <a href> → <button onClick>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
05ccabfdb4
commit
1ecd7dad82
6 changed files with 38 additions and 25 deletions
|
|
@ -5,7 +5,6 @@ import {
|
||||||
Query,
|
Query,
|
||||||
Res,
|
Res,
|
||||||
Logger,
|
Logger,
|
||||||
UseGuards,
|
|
||||||
Param,
|
Param,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||||
|
|
@ -53,15 +52,15 @@ export class IntegrationsController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/v1/auth/integrations/microsoft-365
|
* GET /api/v1/auth/integrations/microsoft-365
|
||||||
* OAuth2-Flow fuer Microsoft 365 starten.
|
* OAuth2-Flow fuer Microsoft 365 vorbereiten.
|
||||||
* User muss eingeloggt sein — userId wird im State gespeichert.
|
* Gibt die Microsoft-Authorisierungs-URL als JSON zurueck.
|
||||||
|
* Das Frontend leitet den Browser dann client-seitig weiter (axios + window.location).
|
||||||
*/
|
*/
|
||||||
@Get('auth/integrations/microsoft-365')
|
@Get('auth/integrations/microsoft-365')
|
||||||
@ApiOperation({ summary: 'Microsoft 365 Integration starten' })
|
@ApiOperation({ summary: 'Microsoft 365 Integration starten' })
|
||||||
async initM365Integration(
|
async initM365Integration(
|
||||||
@CurrentUser() user: JwtUser,
|
@CurrentUser() user: JwtUser,
|
||||||
@Res() res: Response,
|
): Promise<{ success: boolean; data: { url: string } }> {
|
||||||
): Promise<void> {
|
|
||||||
// State = UUID, in Redis mit userId hinterlegen (5 Min TTL)
|
// State = UUID, in Redis mit userId hinterlegen (5 Min TTL)
|
||||||
const state = uuidv4();
|
const state = uuidv4();
|
||||||
await this.redis.set(
|
await this.redis.set(
|
||||||
|
|
@ -74,7 +73,7 @@ export class IntegrationsController {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`M365-Integration OAuth-Flow gestartet fuer User ${user.sub}`,
|
`M365-Integration OAuth-Flow gestartet fuer User ${user.sub}`,
|
||||||
);
|
);
|
||||||
res.redirect(authUrl);
|
return { success: true, data: { url: authUrl } };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -771,8 +771,15 @@ export const integrationsApi = {
|
||||||
)
|
)
|
||||||
.then((r) => r.data),
|
.then((r) => r.data),
|
||||||
|
|
||||||
/** Gibt die URL zurück, zu der der Browser weitergeleitet werden soll */
|
/** Ruft die Microsoft OAuth-URL via API ab (JWT-geschützt) und leitet den Browser weiter */
|
||||||
getM365ConnectUrl: (): string => '/api/v1/auth/integrations/microsoft-365',
|
connectM365: () =>
|
||||||
|
api
|
||||||
|
.get<{ success: boolean; data: { url: string } }>(
|
||||||
|
'/auth/integrations/microsoft-365',
|
||||||
|
)
|
||||||
|
.then((r) => {
|
||||||
|
window.location.href = r.data.data.url;
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Microsoft Graph Proxy (CRM) ---
|
// --- Microsoft Graph Proxy (CRM) ---
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,9 @@ export function CalendarTab({ contactId }: Props) {
|
||||||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||||
Verbinden Sie Microsoft 365, um Kalendertermine zu diesem Kontakt zu sehen.
|
Verbinden Sie Microsoft 365, um Kalendertermine zu diesem Kontakt zu sehen.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<button
|
||||||
href={integrationsApi.getM365ConnectUrl()}
|
type="button"
|
||||||
|
onClick={() => integrationsApi.connectM365()}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -41,14 +42,15 @@ export function CalendarTab({ contactId }: Props) {
|
||||||
padding: '0.4375rem 1rem',
|
padding: '0.4375rem 1rem',
|
||||||
background: 'var(--color-primary)',
|
background: 'var(--color-primary)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
borderRadius: 'var(--radius-sm)',
|
borderRadius: 'var(--radius-sm)',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textDecoration: 'none',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Microsoft 365 verbinden
|
Microsoft 365 verbinden
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ export function EmailsTab({ contactId }: Props) {
|
||||||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||||
Verbinden Sie Microsoft 365, um E-Mails zu diesem Kontakt zu sehen.
|
Verbinden Sie Microsoft 365, um E-Mails zu diesem Kontakt zu sehen.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<button
|
||||||
href={integrationsApi.getM365ConnectUrl()}
|
type="button"
|
||||||
|
onClick={() => integrationsApi.connectM365()}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -40,14 +41,15 @@ export function EmailsTab({ contactId }: Props) {
|
||||||
padding: '0.4375rem 1rem',
|
padding: '0.4375rem 1rem',
|
||||||
background: 'var(--color-primary)',
|
background: 'var(--color-primary)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
borderRadius: 'var(--radius-sm)',
|
borderRadius: 'var(--radius-sm)',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textDecoration: 'none',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Microsoft 365 verbinden
|
Microsoft 365 verbinden
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,9 @@ export function TasksTab({ contactId }: Props) {
|
||||||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||||
Verbinden Sie Microsoft 365, um Aufgaben zu diesem Kontakt zu sehen.
|
Verbinden Sie Microsoft 365, um Aufgaben zu diesem Kontakt zu sehen.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<button
|
||||||
href={integrationsApi.getM365ConnectUrl()}
|
type="button"
|
||||||
|
onClick={() => integrationsApi.connectM365()}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -126,14 +127,15 @@ export function TasksTab({ contactId }: Props) {
|
||||||
padding: '0.4375rem 1rem',
|
padding: '0.4375rem 1rem',
|
||||||
background: 'var(--color-primary)',
|
background: 'var(--color-primary)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
borderRadius: 'var(--radius-sm)',
|
borderRadius: 'var(--radius-sm)',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textDecoration: 'none',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Microsoft 365 verbinden
|
Microsoft 365 verbinden
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -843,9 +843,9 @@ export function ProfilePage() {
|
||||||
Verbinden Sie Ihr Microsoft 365 Konto, um E-Mails, Kalendertermine und Aufgaben
|
Verbinden Sie Ihr Microsoft 365 Konto, um E-Mails, Kalendertermine und Aufgaben
|
||||||
direkt in Kontaktprofilen zu sehen.
|
direkt in Kontaktprofilen zu sehen.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<button
|
||||||
href={integrationsApi.getM365ConnectUrl()}
|
type="button"
|
||||||
className={styles.buttonPrimary ?? undefined}
|
onClick={() => integrationsApi.connectM365()}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -853,17 +853,18 @@ export function ProfilePage() {
|
||||||
padding: '0.5rem 1.25rem',
|
padding: '0.5rem 1.25rem',
|
||||||
background: 'var(--color-primary)',
|
background: 'var(--color-primary)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
borderRadius: 'var(--radius-sm)',
|
borderRadius: 'var(--radius-sm)',
|
||||||
fontSize: '0.9375rem',
|
fontSize: '0.9375rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textDecoration: 'none',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||||
<path d="M0 0h7.5v7.5H0zm8.5 0H16v7.5H8.5zM0 8.5h7.5V16H0zm8.5 0H16V16H8.5z" />
|
<path d="M0 0h7.5v7.5H0zm8.5 0H16v7.5H8.5zM0 8.5h7.5V16H0zm8.5 0H16V16H8.5z" />
|
||||||
</svg>
|
</svg>
|
||||||
Microsoft 365 verbinden
|
Microsoft 365 verbinden
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue