Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

missing /authorize endpoint ? #188

Open
2 tasks done
reactima opened this issue Sep 20, 2024 · 9 comments
Open
2 tasks done

missing /authorize endpoint ? #188

reactima opened this issue Sep 20, 2024 · 9 comments
Labels
improvement login Login Application

Comments

@reactima
Copy link

Preflight Checklist

  • I could not find a solution in the existing issues, docs, nor discussions
  • I have joined the ZITADEL chat

Describe your problem

Read Support for the OpenID Connect(OIDC) Standard in a Custom Login UI
https://zitadel.com/docs/guides/integrate/login-ui/oidc-standard

zitadel/typescript repo seems missing /authorize endpoint

so you have to go through ${ZITADEL_URL}/oauth/v2/authorize endpoint

getting authRequestId is puzzling

Let's say I successful generated Zitadel Authorization URL:

http://localhost:8080/oauth/v2/authorize?client_id=285778005274460424%40rachel_rich_and_happy_&nonce=&prompt=none&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fzitadel%2Floginname&response_type=code&scope=openid+profile+email&state=

It will put me through Zitadel UI anyway be forcing redirect

Couldnt find a straight forward way getting authRequestId

Is missing /authorize endpoint in this repo?

Describe your ideal solution

implement /authorize endpoint

Version

latest

Environment

Self-hosted

Additional Context

No response

@fforootd
Copy link
Member

Our general idea here is that the login-ui only "proxies" the request to the api so that the login does not need to take care of the oidc specific stuff.

I think the zitadel/typescript should already be able to proxy the authorize path.

Have you checked what happens when you call ${LOGIN_URL}/oauth/v2/authorize

@fforootd
Copy link
Member

Might relate to this #46

@reactima
Copy link
Author

reactima commented Sep 24, 2024

Our general idea here is that the login-ui only "proxies" the request to the api so that the login does not need to take care of the oidc specific stuff.

@fforootd thank you for making it clear. really appreciate it

i ended up doing proxing similar to

https://github.com/zitadel/typescript/blob/main/apps/login/src/middleware.ts

export const config = {
  matcher: [
    "/.well-known/:path*",
    "/oauth/:path*",
    "/oidc/:path*",
    "/idps/callback/:path*",
  ],
};

but the ideal situation for me is to get rid of all proxying in favor of backend api calls

i use this repo mainly as an inspiration

what i'm trying to archive

  • want to use golang on backend ... cause terrible experience with nextjs
  • want to reduce frontend footprints to the minimum - dont want to load two UIs one is mine(hosted) and another from zitadel
    • zitadel/typescript repo relies on protobuf generated lib and its bandle size is bloated from my perspective
  • dont want endusers to stack in zitadel ui ... zitadel as a backend service is ok. zitadel ui for admin purpose is really great

i might be moving into a wrong direction and eventually will end up with lot of hacking or some compromises

as for now i'm facing issues like on screenshot below - the enduser stack in zitadel ui, not inside my hosted app

image

and the point is that proxing is not really great user experience ... at least for me ... if you have well defined api and an example how to customize it

and do something similar to

func (h *Handler) RegisterConnections(api huma.API) {
	huma.Register(api,
		huma.Operation{
			Path:          "/oauth2/callback",
			OperationID:   "oauth2-client-callback",
			Description:   "Serve OAuth2 client callback",
			Method:        http.MethodPost,
			Tags:          []string{"oauth2-auth"},
			DefaultStatus: http.StatusOK,
		}, h.callback)
}

func (h *Handler) callback(ctx context.Context, in *CallbackIn) (out *CallbackOut, err error) {
	tx := query.New(h.dbp)
	stateUUID := uuid.FromStringOrNil(in.State)
	if stateUUID.IsNil() {
		h.log.Error("broken state parameter", "error", err)
		return nil, huma.Error400BadRequest("broken state parameter")
	}

	state, err := tx.GetOauth2State(ctx, stateUUID)
	if err != nil {
		h.log.Error("query state object", "error", err)
		return nil, huma.Error401Unauthorized("query state object")
	}
	...

to bring the standard endpoints into hosted app by triggering apis calls

@fforootd
Copy link
Member

Can you exapand what you mean with "but the ideal situation for me is to get rid of all proxying in favor of backend api calls"

Generally speaking the proxy piece is only needed if you want to serve OIDC/OAuth from your login to your apps.

With what stack are you building your login?

@reactima
Copy link
Author

reactima commented Sep 25, 2024

@fforootd

With what stack are you building your login?
front-end ant design (rewriting UI components is the easiest part for me)
back-end golang, postgres

Can you expand what you mean with "but the ideal situation for me is to get rid of all proxying in favor of backend api calls"

i thought it would be possible to do embeded callbacks or customize endpoints similar to router on ui/login

https://github.com/zitadel/zitadel/blob/main/internal/api/ui/login/router.go

EndpointExternalLogin = "/login/externalidp"
EndpointExternalLoginCallback = "/login/externalidp/callback"
EndpointExternalLoginCallbackFormPost = "/login/externalidp/callback/form"

i'm a half way through reading external_provider_handler.go
https://github.com/zitadel/zitadel/blob/main/internal/api/ui/login/external_provider_handler.go

and LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
https://github.com/zitadel/zitadel/blob/main/internal/api/oidc/server.go

and it's look like it's impossible to replicate by using SDK zitadel/zitadel-go sdk as well, cause not all methods which are used in external_provider_handler.go are exposed or mean to be used outside of ui/login

the goal of all the above for now is to understand if i can

  • hide zitadel ui and backend from outside visitors ... as its always great to reduce attack surface
  • reduce the amount of javascript code exposed to enduser
  • if you do embeded login, there should be no situation where enduser will hit unknown UI with duplicated functionality
  • /ui/login code has lot of places with "l.renderError(w, r, authReq, err)"

I'm looking for "api-first" solution for embeded login

Here's an example of function I can't trigger using zitadel-go ... or might miss something

https://github.com/zitadel/zitadel/blob/main/internal/api/oidc/auth_request.go#L92
func (o *OPStorage) createAuthRequestLoginClient(ctx context.Context, req *oidc.AuthRequest, hintUserID, loginClient string) (op.AuthRequest, error) {
func (c *Commands) AddAuthRequest(ctx context.Context, authRequest *AuthRequest)

https://github.com/zitadel/zitadel/blob/main/internal/api/grpc/oidc/v2/oidc.go

Again i'm a half way through reading ... any hints are greatly appreciated

@fforootd
Copy link
Member

I see.

Let me try and explain our approach here.

Our current mental model is to seperate the login from the api in a way that the login ui (whatever language it uses) only needs to implement the user facing flows. Like login, reset, register,.... At the same time we want to keep all the business logic for the OIDC/OAuth/SAML in the API and allow the UI to just call the API as a proxy.

It is also possible to call that api without proxing through the login ui, but we thought in many cases you end up having all this on the same domain behind a proxy where you can directly send the traffic to the api so the proxy in the login becomes obsolete.

@hifabienne hifabienne moved this to 🧐 Investigating in Product Management Sep 30, 2024
@hifabienne hifabienne added the login Login Application label Sep 30, 2024
@larsbloch
Copy link

larsbloch commented Nov 30, 2024

I think im looking for the same setup as reactima. I want the frontend custom ui to ONLY use endpoints in my own backend. So the backend will act like a proxy for some of the requests
Frontend -> Backend -> Zitadel

So by API first you can actually do the whole registrationa and login by using API endpoints of your backend.

Im currently reading up on the issue to check if others are using the same setup and how to handle security like PKCE and such.

@reactima
Copy link
Author

reactima commented Dec 11, 2024

@larsbloch not sure if it will help. it's almost impossible to hide all routes without breaking things.

i get a proof of concept by patching few ui/login routes at some point, and discovered it breaks too many things and puts you on the path of maintaining too many routes

have a look here
https://github.com/zitadel/zitadel/tree/main/internal/api/ui/login

l.renderer.RenderTemplate or renderError - templates are hardcoded

my own conclusion OIDC spec wasn't designed with API first mentality, and mostly serves IDP providers interest ... and i'm totally fine with it.

zitadel team did awesome job - you can customize ui templates from admin ui.

another drawback of deep customization - you will end reading the entire codebase source code ... i did it for pleasure but all my goland devs friends who saw it had their sole crashed

especially in the eventstore
https://github.com/zitadel/zitadel/tree/main/internal/eventstore

p.s. happy to chat with whoever want to go the hard way with zitadel ... and hope one day zitadel's big customers will sponsor true "api first" functionality

@reactima
Copy link
Author

@fforootd thank you for the above explanation. it makes more sense for me now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
improvement login Login Application
Projects
Status: 🧐 Investigating
Development

No branches or pull requests

4 participants