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,
|
||||
Res,
|
||||
Logger,
|
||||
UseGuards,
|
||||
Param,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
|
|
@ -53,15 +52,15 @@ export class IntegrationsController {
|
|||
|
||||
/**
|
||||
* GET /api/v1/auth/integrations/microsoft-365
|
||||
* OAuth2-Flow fuer Microsoft 365 starten.
|
||||
* User muss eingeloggt sein — userId wird im State gespeichert.
|
||||
* OAuth2-Flow fuer Microsoft 365 vorbereiten.
|
||||
* 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')
|
||||
@ApiOperation({ summary: 'Microsoft 365 Integration starten' })
|
||||
async initM365Integration(
|
||||
@CurrentUser() user: JwtUser,
|
||||
@Res() res: Response,
|
||||
): Promise<void> {
|
||||
): Promise<{ success: boolean; data: { url: string } }> {
|
||||
// State = UUID, in Redis mit userId hinterlegen (5 Min TTL)
|
||||
const state = uuidv4();
|
||||
await this.redis.set(
|
||||
|
|
@ -74,7 +73,7 @@ export class IntegrationsController {
|
|||
this.logger.log(
|
||||
`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),
|
||||
|
||||
/** Gibt die URL zurück, zu der der Browser weitergeleitet werden soll */
|
||||
getM365ConnectUrl: (): string => '/api/v1/auth/integrations/microsoft-365',
|
||||
/** Ruft die Microsoft OAuth-URL via API ab (JWT-geschützt) und leitet den Browser weiter */
|
||||
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) ---
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ export function CalendarTab({ contactId }: Props) {
|
|||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||
Verbinden Sie Microsoft 365, um Kalendertermine zu diesem Kontakt zu sehen.
|
||||
</p>
|
||||
<a
|
||||
href={integrationsApi.getM365ConnectUrl()}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => integrationsApi.connectM365()}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
|
@ -41,14 +42,15 @@ export function CalendarTab({ contactId }: Props) {
|
|||
padding: '0.4375rem 1rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 600,
|
||||
textDecoration: 'none',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Microsoft 365 verbinden
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ export function EmailsTab({ contactId }: Props) {
|
|||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||
Verbinden Sie Microsoft 365, um E-Mails zu diesem Kontakt zu sehen.
|
||||
</p>
|
||||
<a
|
||||
href={integrationsApi.getM365ConnectUrl()}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => integrationsApi.connectM365()}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
|
@ -40,14 +41,15 @@ export function EmailsTab({ contactId }: Props) {
|
|||
padding: '0.4375rem 1rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 600,
|
||||
textDecoration: 'none',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Microsoft 365 verbinden
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,8 +117,9 @@ export function TasksTab({ contactId }: Props) {
|
|||
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.9375rem', marginBottom: '1rem' }}>
|
||||
Verbinden Sie Microsoft 365, um Aufgaben zu diesem Kontakt zu sehen.
|
||||
</p>
|
||||
<a
|
||||
href={integrationsApi.getM365ConnectUrl()}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => integrationsApi.connectM365()}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
|
@ -126,14 +127,15 @@ export function TasksTab({ contactId }: Props) {
|
|||
padding: '0.4375rem 1rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 600,
|
||||
textDecoration: 'none',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Microsoft 365 verbinden
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -843,9 +843,9 @@ export function ProfilePage() {
|
|||
Verbinden Sie Ihr Microsoft 365 Konto, um E-Mails, Kalendertermine und Aufgaben
|
||||
direkt in Kontaktprofilen zu sehen.
|
||||
</p>
|
||||
<a
|
||||
href={integrationsApi.getM365ConnectUrl()}
|
||||
className={styles.buttonPrimary ?? undefined}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => integrationsApi.connectM365()}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
|
|
@ -853,17 +853,18 @@ export function ProfilePage() {
|
|||
padding: '0.5rem 1.25rem',
|
||||
background: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 'var(--radius-sm)',
|
||||
fontSize: '0.9375rem',
|
||||
fontWeight: 600,
|
||||
textDecoration: 'none',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<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" />
|
||||
</svg>
|
||||
Microsoft 365 verbinden
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue