Skip to content

Commit 32e899e

Browse files
committed
feat: allow for prompt to skip authorize screen
1 parent 80bb4f1 commit 32e899e

5 files changed

Lines changed: 60 additions & 19 deletions

File tree

frontend/src/lib/hooks/screen-params.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type ScreenParams = {
66
oidc_ticket?: string;
77
oidc_scope?: string;
88
oidc_name?: string;
9-
oidc_login?: boolean;
9+
oidc_prompt?: "none" | "login";
1010
};
1111

1212
const zodScreenParams = z.object({
@@ -15,7 +15,7 @@ const zodScreenParams = z.object({
1515
oidc_ticket: z.string().optional(),
1616
oidc_scope: z.string().optional(),
1717
oidc_name: z.string().optional(),
18-
oidc_login: z.stringbool().optional(),
18+
oidc_prompt: z.enum(["none", "login"]).optional(),
1919
});
2020

2121
export function useScreenParams(params: URLSearchParams): ScreenParams {

frontend/src/pages/authorize-page.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
recompileScreenParams,
2626
useScreenParams,
2727
} from "@/lib/hooks/screen-params";
28+
import { useEffect } from "react";
2829

2930
type Scope = {
3031
id: string;
@@ -90,7 +91,15 @@ export const AuthorizePage = () => {
9091
const isOidc = screenParams.login_for === "oidc";
9192
const compiledParams = recompileScreenParams(screenParams);
9293

93-
const authorizeMutation = useMutation({
94+
// TODO: maybe a better way to do this
95+
const shouldAutoAuthorize =
96+
auth.authenticated &&
97+
isOidc &&
98+
screenParams.oidc_ticket !== undefined &&
99+
screenParams.oidc_scope !== undefined &&
100+
screenParams.oidc_prompt === "none";
101+
102+
const { mutate: authorizeMutate, isPending: authorizePending } = useMutation({
94103
mutationFn: () => {
95104
return axios.post("/api/oidc/authorize-complete", {
96105
ticket: screenParams.oidc_ticket,
@@ -110,6 +119,12 @@ export const AuthorizePage = () => {
110119
},
111120
});
112121

122+
useEffect(() => {
123+
if (shouldAutoAuthorize) {
124+
authorizeMutate();
125+
}
126+
}, [shouldAutoAuthorize, authorizeMutate]);
127+
113128
if (!isOidc || !screenParams.oidc_ticket || !screenParams.oidc_scope) {
114129
return (
115130
<Navigate
@@ -119,7 +134,7 @@ export const AuthorizePage = () => {
119134
);
120135
}
121136

122-
if (!auth.authenticated || screenParams.oidc_login) {
137+
if (!auth.authenticated || screenParams.oidc_prompt === "login") {
123138
return <Navigate to={`/login${compiledParams}`} replace />;
124139
}
125140

@@ -168,14 +183,15 @@ export const AuthorizePage = () => {
168183
)}
169184
<CardFooter className="flex flex-col items-stretch gap-3">
170185
<Button
171-
onClick={() => authorizeMutation.mutate()}
172-
loading={authorizeMutation.isPending}
186+
onClick={() => authorizeMutate()}
187+
loading={authorizePending}
188+
disabled={shouldAutoAuthorize}
173189
>
174190
{t("authorizeTitle")}
175191
</Button>
176192
<Button
177193
onClick={() => navigate(`/logout${compiledParams}`)}
178-
disabled={authorizeMutation.isPending}
194+
disabled={authorizePending || shouldAutoAuthorize}
179195
variant="outline"
180196
>
181197
{t("cancelTitle")}

frontend/src/pages/login-page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const LoginPage = () => {
6565
const screenParams = useScreenParams(searchParams);
6666
const compiledParams = recompileScreenParams({
6767
...screenParams,
68-
oidc_login: false,
68+
oidc_prompt: undefined,
6969
});
7070
const loginForUrl = useLoginFor({
7171
login_for: screenParams.login_for,
@@ -199,7 +199,7 @@ export const LoginPage = () => {
199199
};
200200
}, [redirectTimer, redirectButtonTimer]);
201201

202-
if (auth.authenticated && !screenParams.oidc_login) {
202+
if (auth.authenticated && screenParams.oidc_prompt !== "login") {
203203
return <Navigate to={loginForUrl} replace />;
204204
}
205205

internal/controller/oidc_controller.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ type ClientCredentials struct {
6969
}
7070

7171
type AuthorizeScreenParams struct {
72-
LoginFor FrontendLoginFor `url:"login_for"`
73-
OIDCTicket string `url:"oidc_ticket"`
74-
OIDCScope string `url:"oidc_scope"`
75-
OIDCName string `url:"oidc_name"`
76-
OIDCLogin bool `url:"oidc_login"`
72+
LoginFor FrontendLoginFor `url:"login_for"`
73+
OIDCTicket string `url:"oidc_ticket"`
74+
OIDCScope string `url:"oidc_scope"`
75+
OIDCName string `url:"oidc_name"`
76+
OIDCPrompt service.OIDCPrompt `url:"oidc_prompt,omitempty"`
7777
}
7878

7979
type AuthorizeCompleteRequest struct {
@@ -168,6 +168,8 @@ func (controller *OIDCController) authorize(c *gin.Context) {
168168
return
169169
}
170170

171+
prompt := controller.oidc.GetPrompt(req.Prompt)
172+
171173
userContext, err := new(model.UserContext).NewFromGin(c)
172174

173175
if err != nil {
@@ -176,7 +178,7 @@ func (controller *OIDCController) authorize(c *gin.Context) {
176178
}
177179
}
178180

179-
if (err != nil || !userContext.Authenticated) && req.Prompt == "none" {
181+
if (err != nil || !userContext.Authenticated) && prompt == service.OIDCPromptNone {
180182
controller.authorizeError(c, authorizeErrorParams{
181183
err: errors.New("user not logged in"),
182184
reason: "User not logged in",
@@ -195,10 +197,7 @@ func (controller *OIDCController) authorize(c *gin.Context) {
195197
OIDCTicket: ticket,
196198
OIDCScope: req.Scope,
197199
OIDCName: client.Name,
198-
}
199-
200-
if req.Prompt == "login" {
201-
values.OIDCLogin = true
200+
OIDCPrompt: prompt,
202201
}
203202

204203
queries, err := query.Values(values)

internal/service/oidc_service.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ var (
4444
ErrInvalidClient = errors.New("invalid_client")
4545
)
4646

47+
type OIDCPrompt string
48+
49+
const (
50+
OIDCPromptLogin OIDCPrompt = "login"
51+
OIDCPromptNone OIDCPrompt = "none"
52+
)
53+
54+
var SupportedPrompts = []string{string(OIDCPromptLogin), string(OIDCPromptNone)}
55+
4756
// This is not spec-compliant, the ID token SHOULD NOT contain user info claims but,
4857
// it has became a "standard" and apps are looking for the claims in the ID tokens
4958
// instead of calling the userinfo endpoint, so we include them in the ID token as well
@@ -937,3 +946,20 @@ func (service *OIDCService) DecodeAuthorizeJWT(tokenString string) (*AuthorizeRe
937946
Prompt: get("prompt"),
938947
}, nil
939948
}
949+
950+
// Return the first prompt in the list of prompts, or an empty string if no prompt is specified
951+
func (service *OIDCService) GetPrompt(prompt string) OIDCPrompt {
952+
if prompt == "" {
953+
return ""
954+
}
955+
956+
prompts := strings.Split(prompt, " ")
957+
958+
for _, p := range prompts {
959+
if slices.Contains(SupportedPrompts, p) {
960+
return OIDCPrompt(p)
961+
}
962+
}
963+
964+
return ""
965+
}

0 commit comments

Comments
 (0)