@authrim/sveltekit 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Handoff SSO Handler for SvelteKit
|
|
3
|
+
*
|
|
4
|
+
* Handles handoff token verification and session management for cross-domain SSO
|
|
5
|
+
* in SvelteKit server-side code.
|
|
6
|
+
*/
|
|
7
|
+
import type { Handle, RequestEvent } from '@sveltejs/kit';
|
|
8
|
+
import type { Session, User } from '@authrim/core';
|
|
9
|
+
import { type ServerSessionManagerOptions } from './session.js';
|
|
10
|
+
/**
|
|
11
|
+
* Handoff verification options
|
|
12
|
+
*/
|
|
13
|
+
export interface HandoffVerifyOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Authrim IdP URL (required if not using createAuthHandle)
|
|
16
|
+
*/
|
|
17
|
+
issuer?: string;
|
|
18
|
+
/**
|
|
19
|
+
* OAuth client ID (required if not using createAuthHandle)
|
|
20
|
+
*/
|
|
21
|
+
clientId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Handoff verify endpoint
|
|
24
|
+
* @default '/auth/external/handoff/verify'
|
|
25
|
+
*/
|
|
26
|
+
verifyEndpoint?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Error redirect path
|
|
29
|
+
* @default '/login'
|
|
30
|
+
*/
|
|
31
|
+
errorRedirect?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Session manager options
|
|
34
|
+
*/
|
|
35
|
+
sessionOptions?: ServerSessionManagerOptions;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Verify handoff token from callback URL
|
|
39
|
+
*
|
|
40
|
+
* Usage in +page.server.ts:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* export const load = async (event) => {
|
|
43
|
+
* const result = await verifyHandoffToken(event);
|
|
44
|
+
* if (!result.success) {
|
|
45
|
+
* throw redirect(302, '/login');
|
|
46
|
+
* }
|
|
47
|
+
* return { session: result.session, user: result.user };
|
|
48
|
+
* };
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function verifyHandoffToken(event: RequestEvent, options?: HandoffVerifyOptions): Promise<{
|
|
52
|
+
success: true;
|
|
53
|
+
session: Session;
|
|
54
|
+
user: User;
|
|
55
|
+
} | {
|
|
56
|
+
success: false;
|
|
57
|
+
error: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Create handoff handler for SvelteKit hooks
|
|
61
|
+
*
|
|
62
|
+
* Usage in hooks.server.ts:
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { sequence } from '@sveltejs/kit/hooks';
|
|
65
|
+
* import { createAuthHandle, createHandoffHandler } from '@authrim/sveltekit/server';
|
|
66
|
+
*
|
|
67
|
+
* export const handle = sequence(
|
|
68
|
+
* createAuthHandle({
|
|
69
|
+
* issuer: env.AUTHRIM_ISSUER,
|
|
70
|
+
* clientId: env.AUTHRIM_CLIENT_ID,
|
|
71
|
+
* }),
|
|
72
|
+
* createHandoffHandler()
|
|
73
|
+
* );
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function createHandoffHandler(options?: HandoffVerifyOptions): Handle;
|
|
77
|
+
//# sourceMappingURL=handoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../src/lib/server/handoff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,cAAc,CAAC,EAAE,2BAA2B,CAAC;CAC9C;AAmDD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CACN;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC/C;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CACpC,CA+DA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAwB3E"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Handoff SSO Handler for SvelteKit
|
|
3
|
+
*
|
|
4
|
+
* Handles handoff token verification and session management for cross-domain SSO
|
|
5
|
+
* in SvelteKit server-side code.
|
|
6
|
+
*/
|
|
7
|
+
import { redirect } from '@sveltejs/kit';
|
|
8
|
+
import { createServerSessionManager, } from './session.js';
|
|
9
|
+
/**
|
|
10
|
+
* Get auth config from options or event.locals
|
|
11
|
+
*/
|
|
12
|
+
function getAuthConfig(event, options) {
|
|
13
|
+
// Try to get from options first
|
|
14
|
+
if (options?.issuer && options?.clientId) {
|
|
15
|
+
return { issuer: options.issuer, clientId: options.clientId };
|
|
16
|
+
}
|
|
17
|
+
// Fallback to event.locals (set by createAuthHandle)
|
|
18
|
+
const issuer = event.locals.authrim_issuer;
|
|
19
|
+
const clientId = event.locals.authrim_client_id;
|
|
20
|
+
if (!issuer || !clientId) {
|
|
21
|
+
throw new Error('Authrim config not found. Please provide issuer and clientId in options, or use createAuthHandle.');
|
|
22
|
+
}
|
|
23
|
+
return { issuer, clientId };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Verify handoff token from callback URL
|
|
27
|
+
*
|
|
28
|
+
* Usage in +page.server.ts:
|
|
29
|
+
* ```typescript
|
|
30
|
+
* export const load = async (event) => {
|
|
31
|
+
* const result = await verifyHandoffToken(event);
|
|
32
|
+
* if (!result.success) {
|
|
33
|
+
* throw redirect(302, '/login');
|
|
34
|
+
* }
|
|
35
|
+
* return { session: result.session, user: result.user };
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export async function verifyHandoffToken(event, options) {
|
|
40
|
+
const { url } = event;
|
|
41
|
+
const handoffToken = url.searchParams.get('handoff_token');
|
|
42
|
+
const state = url.searchParams.get('state');
|
|
43
|
+
if (!handoffToken || !state) {
|
|
44
|
+
return { success: false, error: 'Missing handoff token or state' };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const { issuer, clientId } = getAuthConfig(event, options);
|
|
48
|
+
const verifyEndpoint = options?.verifyEndpoint || '/auth/external/handoff/verify';
|
|
49
|
+
const response = await fetch(`${issuer}${verifyEndpoint}`, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
handoff_token: handoffToken,
|
|
54
|
+
state: state,
|
|
55
|
+
client_id: clientId,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const error = await response.json().catch(() => ({}));
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: error.error_description || 'Handoff verification failed',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
// Save session to cookie
|
|
67
|
+
const sessionManager = createServerSessionManager(options?.sessionOptions);
|
|
68
|
+
const authContext = {
|
|
69
|
+
session: {
|
|
70
|
+
id: data.session.id,
|
|
71
|
+
userId: data.session.userId,
|
|
72
|
+
createdAt: data.session.createdAt,
|
|
73
|
+
expiresAt: data.session.expiresAt,
|
|
74
|
+
},
|
|
75
|
+
user: {
|
|
76
|
+
id: data.user.id,
|
|
77
|
+
email: data.user.email ?? undefined,
|
|
78
|
+
name: data.user.name ?? undefined,
|
|
79
|
+
emailVerified: data.user.emailVerified,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
sessionManager.set(event, authContext);
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
session: authContext.session,
|
|
86
|
+
user: authContext.user,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create handoff handler for SvelteKit hooks
|
|
98
|
+
*
|
|
99
|
+
* Usage in hooks.server.ts:
|
|
100
|
+
* ```typescript
|
|
101
|
+
* import { sequence } from '@sveltejs/kit/hooks';
|
|
102
|
+
* import { createAuthHandle, createHandoffHandler } from '@authrim/sveltekit/server';
|
|
103
|
+
*
|
|
104
|
+
* export const handle = sequence(
|
|
105
|
+
* createAuthHandle({
|
|
106
|
+
* issuer: env.AUTHRIM_ISSUER,
|
|
107
|
+
* clientId: env.AUTHRIM_CLIENT_ID,
|
|
108
|
+
* }),
|
|
109
|
+
* createHandoffHandler()
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function createHandoffHandler(options) {
|
|
114
|
+
return async ({ event, resolve }) => {
|
|
115
|
+
// Check if this is a handoff callback
|
|
116
|
+
const { url } = event;
|
|
117
|
+
if (url.searchParams.has('handoff_token')) {
|
|
118
|
+
const result = await verifyHandoffToken(event, options);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
const errorRedirect = options?.errorRedirect || '/login';
|
|
121
|
+
// Use 303 See Other (POST → GET redirect, safer than 302)
|
|
122
|
+
throw redirect(303, errorRedirect);
|
|
123
|
+
}
|
|
124
|
+
// Remove handoff token from URL
|
|
125
|
+
const cleanUrl = new URL(url);
|
|
126
|
+
cleanUrl.searchParams.delete('handoff_token');
|
|
127
|
+
cleanUrl.searchParams.delete('state');
|
|
128
|
+
// Use 303 See Other (認証後のリダイレクトに適切)
|
|
129
|
+
throw redirect(303, cleanUrl.toString());
|
|
130
|
+
}
|
|
131
|
+
return resolve(event);
|
|
132
|
+
};
|
|
133
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createServerSessionManager, type ServerSessionManager, type ServerSessionManagerOptions, type ServerAuthContext, } from './session.js';
|
|
2
2
|
export { createAuthHandle, getServerSessionManager, getAuthFromEvent, type AuthHandleOptions, } from './handle.js';
|
|
3
3
|
export { requireAuth, createAuthLoad, isAuthenticated, getUser, getSession, type AuthLoadOptions, } from './load.js';
|
|
4
|
+
export { verifyHandoffToken, createHandoffHandler, type HandoffVerifyOptions, } from './handoff.js';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,cAAc,EACd,eAAe,EACf,OAAO,EACP,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,cAAc,EACd,eAAe,EACf,OAAO,EACP,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { createServerSessionManager, } from './session.js';
|
|
2
2
|
export { createAuthHandle, getServerSessionManager, getAuthFromEvent, } from './handle.js';
|
|
3
3
|
export { requireAuth, createAuthLoad, isAuthenticated, getUser, getSession, } from './load.js';
|
|
4
|
+
export { verifyHandoffToken, createHandoffHandler, } from './handoff.js';
|