Force login using SSO
Is your feature request related to a problem?
I use the OIDC provider to provide authentication services for the memo, but every time I log in, I will still be redirected to the login page of the memos itself.
Describe the solution you'd like
- Provide a switch in the settings to specify an SSO provider as the default login provider, and directly initiate OIDC authentication instead of popping up the built-in login interface when login is required.
- Provide a url parameter sso for /auth. When its value is false, it is allowed to use the default interface to log in (used for troubleshooting when the third-party provider fails to log in due to an exception).
Additional context
Currently, I complete the jump by inserting a piece of js in the login interface, identifying the login interface with a frequency of 1 second, and forcibly triggering SSO login. Although this method fulfills my needs to a certain extent, the implementation method is very disgusting, and before the jump, the user can still see the built-in login interface for about 1-3 seconds.
NOTE: This script is out of date with current (as of July 2024) versions of Memos. See my comment later in the thread for an updated version.
I'd also +1 support for this as a built-in feature. As a workaround similar to the original poster, here is the script that I use in additional scripts to force an immediate redirect from initial load of any page to my SSO login. This works by making a call to the Memos API user endpoint to see if there's a user logged in, and if not then redirect.
(async function() {
// Remember to replace this authentik style placeholder URL with your own.
const LOGIN_URL = 'https://AUTHENTIK_BASE_URL/application/o/authorize/?client_id=MEMOS_CLIENT_ID&redirect_uri=https://MEMOS_BASE_URL/auth/callback&state=auth.signin.Authentik-1&response_type=code&scope=openid profile email'
// Prevent a redirect loop by not checking login state from
// login callback path.
if (window.location.pathname !== "/auth/callback") {
// Call Memos API to determine if the user is logged in.
// If logged in, HTTP status is 200, if logged out 401.
const status = (await fetch("/api/v1/user/me")).status;
if (status === 401) {
window.location = LOGIN_URL;
}
}
})();
I use Authentik as my SSO provider, so LOGIN_URL here will vary based on what your provider is. To get the exact URL, I recommend opening up an incognito window, opening developer tools, selecting the network tab and enabling "persist logs" from the settings menu, and then clicking the "Login with <YOUR PROVIDER>" link from Memos. The first URL to your auth provider in the network tab will be the one you want to use.
Agree this would be very useful feature for anyone who's gone through the trouble of setting up a SSO provider
@justjohn can confirm your workaround functions with other auth providers, and thank you so much for including the extra details of how to find that URL lol would have never been able to do that myself
Disable user signup + Disable password login might help.
@K-J-VV I expect my workaround will work with any OIDC provider, although I can't speak directly to anything besides Authentik, since that's what I use. The protocol is a standard though, so they all operate about the same. The main difference should just be the login URL that you redirect to since it will differ between providers.
I'd also +1 support for this as a built-in feature. As a workaround similar to the original poster, here is the script that I use in additional scripts to force an immediate redirect from initial load of any page to my SSO login. This works by making a call to the Memos API user endpoint to see if there's a user logged in, and if not then redirect.
(async function() { // Remember to replace this authentik style placeholder URL with your own. const LOGIN_URL = 'https://AUTHENTIK_BASE_URL/application/o/authorize/?client_id=MEMOS_CLIENT_ID&redirect_uri=https://MEMOS_BASE_URL/auth/callback&state=auth.signin.Authentik-1&response_type=code&scope=openid profile email' // Prevent a redirect loop by not checking login state from // login callback path. if (window.location.pathname !== "/auth/callback") { // Call Memos API to determine if the user is logged in. // If logged in, HTTP status is 200, if logged out 401. const status = (await fetch("/api/v1/user/me")).status; if (status === 401) { window.location = LOGIN_URL; } } })();I use Authentik as my SSO provider, so
LOGIN_URLhere will vary based on what your provider is. To get the exact URL, I recommend opening up an incognito window, opening developer tools, selecting the network tab and enabling "persist logs" from the settings menu, and then clicking the "Login with " link from Memos. The first URL to your auth provider in the network tab will be the one you want to use.
I'm not sure if this thread is just too old at this point, or the API has changed, but this code does not work. Mind you, I do have things running through Cloudflare proxy, so not sure if that is complicating things. If anyone could offer any suggestions, that would be greatly appreciated.
It looks Memos API has changed since I wrote that script, (the test I was using fetch("/api/v1/user/me") no longer works since the internal API seems to have moved to use grpc.
It's not as pretty, but this works for me with the new version of the API:
(async function() {
// Remember to replace this authentik style placeholder URL with your own.
const LOGIN_URL = 'https://AUTHENTIK_BASE_URL/application/o/authorize/?client_id=MEMOS_CLIENT_ID&redirect_uri=https://MEMOS_BASE_URL/auth/callback&state=auth.signin.Authentik-1&response_type=code&scope=openid profile email'
// Prevent a redirect loop by not checking login state from
// login callback path.
if (window.location.pathname !== "/auth/callback") {
// Call Memos API to determine if the user is logged in.
// If logged in, this returns data, if logged out it returns no data.
const response = (await (await fetch("/memos.api.v1.UserService/GetUserSetting", {
method: "POST",
// This magic string is copied from a real request after logging in.
body: "\u0000\u0000\u0000\u0000\u0000",
headers: {"Content-Type": "application/grpc-web+proto"}
})).text());
if (response.length === 0) {
window.location = LOGIN_URL;
}
}
})();
It looks Memos API has changed since I wrote that script, (the test I was using
fetch("/api/v1/user/me")no longer works since the internal API seems to have moved to use grpc.It's not as pretty, but this works for me with the new version of the API:
(async function() { // Remember to replace this authentik style placeholder URL with your own. const LOGIN_URL = 'https://AUTHENTIK_BASE_URL/application/o/authorize/?client_id=MEMOS_CLIENT_ID&redirect_uri=https://MEMOS_BASE_URL/auth/callback&state=auth.signin.Authentik-1&response_type=code&scope=openid profile email' // Prevent a redirect loop by not checking login state from // login callback path. if (window.location.pathname !== "/auth/callback") { // Call Memos API to determine if the user is logged in. // If logged in, this returns data, if logged out it returns no data. const response = (await (await fetch("/memos.api.v1.UserService/GetUserSetting", { method: "POST", // This magic string is copied from a real request after logging in. body: "\u0000\u0000\u0000\u0000\u0000", headers: {"Content-Type": "application/grpc-web+proto"} })).text()); if (response.length === 0) { window.location = LOGIN_URL; } } })();
Works perfectly - thank you so much!
It's not as pretty, but this works for me with the new version of the API: @justjohn -- Amazing work - this works flawlessly! Only one issue... I couldn't elevate my Authentik SSO account to Admin within Memos. I still have the credentials to login as original Admin...but to do that, I need to be able to login without SSO. Any workaround to temporarily disable the script?