Add JWT token handling to document viewer and related components
This commit is contained in:
@@ -34,6 +34,7 @@ class DocumentViewerBloc
|
|||||||
DocumentViewerReady(
|
DocumentViewerReady(
|
||||||
viewUrl: session.viewUrl,
|
viewUrl: session.viewUrl,
|
||||||
caps: session.capabilities,
|
caps: session.capabilities,
|
||||||
|
token: session.token,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_expiryTimer = Timer(
|
_expiryTimer = Timer(
|
||||||
|
|||||||
@@ -15,11 +15,16 @@ class DocumentViewerLoading extends DocumentViewerState {}
|
|||||||
class DocumentViewerReady extends DocumentViewerState {
|
class DocumentViewerReady extends DocumentViewerState {
|
||||||
final Uri viewUrl;
|
final Uri viewUrl;
|
||||||
final DocumentCapabilities caps;
|
final DocumentCapabilities caps;
|
||||||
|
final String token;
|
||||||
|
|
||||||
const DocumentViewerReady({required this.viewUrl, required this.caps});
|
const DocumentViewerReady({
|
||||||
|
required this.viewUrl,
|
||||||
|
required this.caps,
|
||||||
|
required this.token,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [viewUrl, caps];
|
List<Object> get props => [viewUrl, caps, token];
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentViewerError extends DocumentViewerState {
|
class DocumentViewerError extends DocumentViewerState {
|
||||||
|
|||||||
@@ -169,19 +169,9 @@ class _DocumentViewerModalState extends State<DocumentViewerModal> {
|
|||||||
}
|
}
|
||||||
if (state is DocumentViewerReady) {
|
if (state is DocumentViewerReady) {
|
||||||
if (state.caps.isPdf) {
|
if (state.caps.isPdf) {
|
||||||
return BlocBuilder<SessionBloc, SessionState>(
|
|
||||||
builder: (context, sessionState) {
|
|
||||||
String? token;
|
|
||||||
if (sessionState is SessionActive) {
|
|
||||||
token = sessionState.token;
|
|
||||||
}
|
|
||||||
return SfPdfViewer.network(
|
return SfPdfViewer.network(
|
||||||
state.viewUrl.toString(),
|
state.viewUrl.toString(),
|
||||||
headers: token != null
|
headers: {'Authorization': 'Bearer ${state.token}'},
|
||||||
? {'Authorization': 'Bearer $token'}
|
|
||||||
: {},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@@ -70,14 +70,7 @@ void main() {
|
|||||||
act: (bloc) => bloc.add(DocumentOpened(orgId: 'org1', fileId: 'file1')),
|
act: (bloc) => bloc.add(DocumentOpened(orgId: 'org1', fileId: 'file1')),
|
||||||
expect: () => [
|
expect: () => [
|
||||||
DocumentViewerLoading(),
|
DocumentViewerLoading(),
|
||||||
DocumentViewerReady(
|
DocumentViewerError(message: 'Failed to open document: Server error'),
|
||||||
viewUrl: Uri.parse('https://example.com/view'),
|
|
||||||
caps: DocumentCapabilities(
|
|
||||||
canEdit: true,
|
|
||||||
canAnnotate: false,
|
|
||||||
isPdf: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,7 +96,15 @@ void main() {
|
|||||||
act: (bloc) => bloc.add(DocumentOpened(orgId: 'org1', fileId: 'file1')),
|
act: (bloc) => bloc.add(DocumentOpened(orgId: 'org1', fileId: 'file1')),
|
||||||
expect: () => [
|
expect: () => [
|
||||||
DocumentViewerLoading(),
|
DocumentViewerLoading(),
|
||||||
DocumentViewerError(message: 'Failed to open document: Server error'),
|
DocumentViewerReady(
|
||||||
|
viewUrl: Uri.parse('https://example.com/view'),
|
||||||
|
caps: DocumentCapabilities(
|
||||||
|
canEdit: true,
|
||||||
|
canAnnotate: false,
|
||||||
|
isPdf: false,
|
||||||
|
),
|
||||||
|
token: 'mock-token',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -371,6 +371,9 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi
|
|||||||
// Determine if it's a PDF based on file extension
|
// Determine if it's a PDF based on file extension
|
||||||
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
||||||
|
|
||||||
|
// Get JWT token from context
|
||||||
|
token, _ := middleware.GetToken(r.Context())
|
||||||
|
|
||||||
session := struct {
|
session := struct {
|
||||||
ViewUrl string `json:"viewUrl"`
|
ViewUrl string `json:"viewUrl"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -382,7 +385,7 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi
|
|||||||
ExpiresAt string `json:"expiresAt"`
|
ExpiresAt string `json:"expiresAt"`
|
||||||
}{
|
}{
|
||||||
ViewUrl: downloadPath,
|
ViewUrl: downloadPath,
|
||||||
Token: userIDStr, // Session token - user is already authenticated via middleware
|
Token: token, // JWT token for authenticating file download
|
||||||
Capabilities: struct {
|
Capabilities: struct {
|
||||||
CanEdit bool `json:"canEdit"`
|
CanEdit bool `json:"canEdit"`
|
||||||
CanAnnotate bool `json:"canAnnotate"`
|
CanAnnotate bool `json:"canAnnotate"`
|
||||||
@@ -424,6 +427,9 @@ func userViewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB,
|
|||||||
// Determine if it's a PDF based on file extension
|
// Determine if it's a PDF based on file extension
|
||||||
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
||||||
|
|
||||||
|
// Get JWT token from context
|
||||||
|
token, _ := middleware.GetToken(r.Context())
|
||||||
|
|
||||||
session := struct {
|
session := struct {
|
||||||
ViewUrl string `json:"viewUrl"`
|
ViewUrl string `json:"viewUrl"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -435,7 +441,7 @@ func userViewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB,
|
|||||||
ExpiresAt string `json:"expiresAt"`
|
ExpiresAt string `json:"expiresAt"`
|
||||||
}{
|
}{
|
||||||
ViewUrl: downloadPath,
|
ViewUrl: downloadPath,
|
||||||
Token: userIDStr, // Session token - user is already authenticated via middleware
|
Token: token, // JWT token for authenticating file download
|
||||||
Capabilities: struct {
|
Capabilities: struct {
|
||||||
CanEdit bool `json:"canEdit"`
|
CanEdit bool `json:"canEdit"`
|
||||||
CanAnnotate bool `json:"canAnnotate"`
|
CanAnnotate bool `json:"canAnnotate"`
|
||||||
@@ -482,12 +488,17 @@ func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi
|
|||||||
// Check if user can edit (for now, all org members can edit)
|
// Check if user can edit (for now, all org members can edit)
|
||||||
readOnly := false
|
readOnly := false
|
||||||
|
|
||||||
|
// Get JWT token from context
|
||||||
|
token, _ := middleware.GetToken(r.Context())
|
||||||
|
|
||||||
session := struct {
|
session := struct {
|
||||||
EditUrl string `json:"editUrl"`
|
EditUrl string `json:"editUrl"`
|
||||||
|
Token string `json:"token"`
|
||||||
ReadOnly bool `json:"readOnly"`
|
ReadOnly bool `json:"readOnly"`
|
||||||
ExpiresAt string `json:"expiresAt"`
|
ExpiresAt string `json:"expiresAt"`
|
||||||
}{
|
}{
|
||||||
EditUrl: collaboraUrl,
|
EditUrl: collaboraUrl,
|
||||||
|
Token: token, // JWT token for authenticating file access
|
||||||
ReadOnly: readOnly,
|
ReadOnly: readOnly,
|
||||||
ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339),
|
ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type contextKey string
|
|||||||
const (
|
const (
|
||||||
userKey contextKey = "user"
|
userKey contextKey = "user"
|
||||||
sessionKey contextKey = "session"
|
sessionKey contextKey = "session"
|
||||||
|
tokenKey contextKey = "token"
|
||||||
orgKey contextKey = "org"
|
orgKey contextKey = "org"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,6 +84,12 @@ func GetSession(ctx context.Context) (*database.Session, bool) {
|
|||||||
return session, ok
|
return session, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetToken retrieves the JWT token from the request context
|
||||||
|
func GetToken(ctx context.Context) (string, bool) {
|
||||||
|
token, ok := ctx.Value(tokenKey).(string)
|
||||||
|
return token, ok
|
||||||
|
}
|
||||||
|
|
||||||
// Auth middleware
|
// Auth middleware
|
||||||
func Auth(jwtManager *jwt.Manager, db *database.DB) func(http.Handler) http.Handler {
|
func Auth(jwtManager *jwt.Manager, db *database.DB) func(http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
@@ -102,6 +109,7 @@ func Auth(jwtManager *jwt.Manager, db *database.DB) func(http.Handler) http.Hand
|
|||||||
|
|
||||||
ctx := context.WithValue(r.Context(), userKey, claims.UserID)
|
ctx := context.WithValue(r.Context(), userKey, claims.UserID)
|
||||||
ctx = context.WithValue(ctx, sessionKey, session)
|
ctx = context.WithValue(ctx, sessionKey, session)
|
||||||
|
ctx = context.WithValue(ctx, tokenKey, tokenString)
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user