mirror of
https://github.com/outline/outline.git
synced 2026-06-13 19:35:02 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a7475ac352 | |||
| a51456deb3 |
@@ -235,7 +235,11 @@ export const logout = createAction({
|
||||
await stores.auth.logout();
|
||||
if (env.OIDC_LOGOUT_URI) {
|
||||
setTimeout(() => {
|
||||
window.location.replace(env.OIDC_LOGOUT_URI);
|
||||
let logoutUri = env.OIDC_LOGOUT_URI;
|
||||
if (stores.auth.idToken && logoutUri.includes("{id_token}")) {
|
||||
logoutUri = logoutUri.replace("{id_token}", stores.auth.idToken);
|
||||
}
|
||||
window.location.replace(logoutUri);
|
||||
}, 200);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,12 @@ const Logout = () => {
|
||||
void auth.logout().then(() => {
|
||||
if (env.OIDC_LOGOUT_URI) {
|
||||
setTimeout(() => {
|
||||
window.location.replace(env.OIDC_LOGOUT_URI);
|
||||
// If the logout URI contains a template for the id_token, replace it
|
||||
let logoutUri = env.OIDC_LOGOUT_URI;
|
||||
if (auth.idToken && logoutUri.includes("{id_token}")) {
|
||||
logoutUri = logoutUri.replace("{id_token}", auth.idToken);
|
||||
}
|
||||
window.location.replace(logoutUri);
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
+10
-1
@@ -18,7 +18,7 @@ import Store from "./base/Store";
|
||||
|
||||
type PersistedData = Pick<
|
||||
AuthStore,
|
||||
"user" | "team" | "collaborationToken" | "availableTeams" | "policies"
|
||||
"user" | "team" | "collaborationToken" | "idToken" | "availableTeams" | "policies"
|
||||
>;
|
||||
|
||||
type Provider = {
|
||||
@@ -50,6 +50,10 @@ export default class AuthStore extends Store<Team> {
|
||||
@observable
|
||||
public collaborationToken?: string | null;
|
||||
|
||||
/* The ID token from OIDC authentication, used for logout */
|
||||
@observable
|
||||
public idToken?: string | null;
|
||||
|
||||
/* A list of teams that the current user has access to. */
|
||||
@observable
|
||||
public availableTeams?: {
|
||||
@@ -133,6 +137,7 @@ export default class AuthStore extends Store<Team> {
|
||||
this.currentTeamId = data.team?.id;
|
||||
this.currentUserId = data.user?.id;
|
||||
this.collaborationToken = data.collaborationToken;
|
||||
this.idToken = data.idToken;
|
||||
this.lastSignedIn = getCookie("lastSignedIn");
|
||||
}
|
||||
|
||||
@@ -171,6 +176,7 @@ export default class AuthStore extends Store<Team> {
|
||||
user: this.user,
|
||||
team: this.team,
|
||||
collaborationToken: this.collaborationToken,
|
||||
idToken: this.idToken,
|
||||
availableTeams: this.availableTeams,
|
||||
policies: this.policies,
|
||||
};
|
||||
@@ -205,6 +211,7 @@ export default class AuthStore extends Store<Team> {
|
||||
|
||||
this.availableTeams = res.data.availableTeams;
|
||||
this.collaborationToken = res.data.collaborationToken;
|
||||
this.idToken = res.data.id_token;
|
||||
|
||||
if (env.SENTRY_DSN) {
|
||||
Sentry.configureScope((scope) => {
|
||||
@@ -261,6 +268,7 @@ export default class AuthStore extends Store<Team> {
|
||||
this.currentUserId = null;
|
||||
this.currentTeamId = null;
|
||||
this.collaborationToken = null;
|
||||
this.idToken = null;
|
||||
this.availableTeams = this.availableTeams?.filter(
|
||||
(team) => team.id !== this.team?.id
|
||||
);
|
||||
@@ -333,6 +341,7 @@ export default class AuthStore extends Store<Team> {
|
||||
this.currentUserId = null;
|
||||
this.currentTeamId = null;
|
||||
this.collaborationToken = null;
|
||||
this.idToken = null;
|
||||
this.rootStore.clear();
|
||||
|
||||
// Tell the host application we logged out, if any – allows window cleanup.
|
||||
|
||||
@@ -58,7 +58,7 @@ if (
|
||||
ctx: Context,
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
params: { expires_in: number },
|
||||
params: { expires_in: number; id_token?: string },
|
||||
_profile: unknown,
|
||||
done: (
|
||||
err: Error | null,
|
||||
@@ -153,7 +153,7 @@ if (
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
return done(null, result.user, { ...result, client });
|
||||
return done(null, result.user, { ...result, client, id_token: params.id_token });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ router.post("auth.info", auth(), async (ctx: APIContext<T.AuthInfoReq>) => {
|
||||
groups: await Promise.all(groups.map(presentGroup)),
|
||||
groupUsers: groups.map((group) => presentGroupUser(group.groupUsers[0])),
|
||||
collaborationToken: user.getCollaborationToken(),
|
||||
id_token: ctx.state.auth.id_token,
|
||||
availableTeams: uniqBy([...signedInTeams, ...availableTeams], "id").map(
|
||||
(availableTeam) =>
|
||||
presentAvailableTeam(
|
||||
|
||||
@@ -45,12 +45,14 @@ export enum AuthenticationType {
|
||||
|
||||
export type AuthenticationResult = AccountProvisionerResult & {
|
||||
client: Client;
|
||||
id_token?: string;
|
||||
};
|
||||
|
||||
export type Authentication = {
|
||||
user: User;
|
||||
token?: string;
|
||||
type?: AuthenticationType;
|
||||
id_token?: string;
|
||||
};
|
||||
|
||||
export type Pagination = {
|
||||
|
||||
@@ -30,7 +30,7 @@ export function getSessionsInCookie(ctx: Context) {
|
||||
export async function signIn(
|
||||
ctx: Context,
|
||||
service: string,
|
||||
{ user, team, client, isNewTeam }: AuthenticationResult
|
||||
{ user, team, client, isNewTeam, id_token }: AuthenticationResult
|
||||
) {
|
||||
if (team.isSuspended) {
|
||||
return ctx.redirect("/?notice=team-suspended");
|
||||
|
||||
@@ -10,6 +10,10 @@ type Props = {
|
||||
captureEvents?: "all" | "pointer" | "click";
|
||||
};
|
||||
|
||||
/**
|
||||
* EventBoundary is a component that prevents events from propagating to parent elements.
|
||||
* This is useful for preventing clicks or other interactions from bubbling up the DOM tree.
|
||||
*/
|
||||
const EventBoundary: React.FC<Props> = ({
|
||||
children,
|
||||
className,
|
||||
|
||||
@@ -5,14 +5,26 @@ type JustifyValues = CSSProperties["justifyContent"];
|
||||
|
||||
type AlignValues = CSSProperties["alignItems"];
|
||||
|
||||
/**
|
||||
* Flex is a styled component that provides a flexible box layout with convenient props.
|
||||
* It simplifies the use of flexbox CSS properties with a clean, declarative API.
|
||||
*/
|
||||
const Flex = styled.div<{
|
||||
/** Makes the component grow to fill available space */
|
||||
auto?: boolean;
|
||||
/** Changes flex direction to column */
|
||||
column?: boolean;
|
||||
/** Sets the align-items CSS property */
|
||||
align?: AlignValues;
|
||||
/** Sets the justify-content CSS property */
|
||||
justify?: JustifyValues;
|
||||
/** Enables flex-wrap */
|
||||
wrap?: boolean;
|
||||
/** Controls flex-shrink behavior */
|
||||
shrink?: boolean;
|
||||
/** Reverses the direction (row-reverse or column-reverse) */
|
||||
reverse?: boolean;
|
||||
/** Sets gap between flex items in pixels */
|
||||
gap?: number;
|
||||
}>`
|
||||
display: flex;
|
||||
|
||||
@@ -11,6 +11,11 @@ type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Squircle is a component that renders a square with rounded corners (squircle shape).
|
||||
* It's commonly used for app icons, avatars, and other UI elements where a softer
|
||||
* square shape is desired.
|
||||
*/
|
||||
const Squircle: React.FC<Props> = ({
|
||||
color,
|
||||
size = 28,
|
||||
|
||||
Reference in New Issue
Block a user