Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5-internal/remove-dead-spar-code
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove binding of users to saml idps using saml (this has never been picked up by clients; use scim instead)
3 changes: 0 additions & 3 deletions charts/nginz/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,6 @@ nginx_conf:
envs:
- staging
versioned: false
- path: /sso-initiate-bind
envs:
- all
- path: /sso/initiate-login
envs:
- all
Expand Down
5 changes: 0 additions & 5 deletions deploy/services-demo/conf/nginz/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -479,11 +479,6 @@ http {
proxy_pass http://spar;
}

location /sso-initiate-bind {
include common_response_with_zauth.conf;
proxy_pass http://spar;
}

location /identity-providers {
include common_response_with_zauth.conf;
proxy_pass http://spar;
Expand Down
6 changes: 0 additions & 6 deletions docs/legacy/reference/spar-braindump.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,6 @@ not lead to a good user experience. so instead we require users to
adopt the more robust and contemporary scim standard.


#### we don't support binding password/phone-auth'ed users to saml yet

to keep track of whether we have, see https://github.com/zinfra/backend-issues/issues/731



## application logic

### deleting users that exist on spar
Expand Down
52 changes: 0 additions & 52 deletions libs/wire-api/src/Wire/API/Cookie.hs

This file was deleted.

68 changes: 2 additions & 66 deletions libs/wire-api/src/Wire/API/Routes/Public/Spar.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ module Wire.API.Routes.Public.Spar where

import Data.Id
import Data.Proxy
import Data.String.Conversions (ST)
import Data.Swagger (Swagger)
import Imports
import qualified SAML2.WebSSO as SAML
Expand All @@ -32,7 +31,6 @@ import qualified URI.ByteString as URI
import Web.Scim.Capabilities.MetaSchema as Scim.Meta
import Web.Scim.Class.Auth as Scim.Auth
import Web.Scim.Class.User as Scim.User
import Wire.API.Cookie
import Wire.API.Error
import Wire.API.Error.Brig
import Wire.API.Routes.Public
Expand All @@ -47,8 +45,6 @@ import Wire.API.User.Scim

type API =
"sso" :> APISSO
:<|> "sso-initiate-bind" :> APIAuthReqPrecheck -- (see comment on 'APIAuthReq')
:<|> "sso-initiate-bind" :> APIAuthReq -- (see comment on 'APIAuthReq')
:<|> "identity-providers" :> APIIDP
:<|> "scim" :> APIScim
:<|> OmitDocs :> "i" :> APIINTERNAL
Expand All @@ -70,82 +66,22 @@ type APIAuthReqPrecheck =
:> Capture "idp" SAML.IdPId
:> CheckOK '[PlainText] NoContent

-- | Dual-use route for initiating either login or bind.
--
-- We could distinguish the two cases by the presence or absence of a `Z-User` header. However, we
-- also need to use this route under two different prefices because nginz only supports mandatory
-- auth and no auth, but not optional auth for any given end-point. See also: 'DoInitiate'.
--
-- __Binding existing users to SSO__
--
-- A user has an existing account with email address and password, and gets idp credentails. She
-- now wants to upgrade her password-based login to sso login, or *bind* the existing user to the
-- sso credentials.
--
-- __The Solution__
--
-- 0. user logs in with password
-- 1. inside the session, she requests `/sso-initiate-bind/<idp>` for an idp of her choice (idp
-- must be registered with her team)
-- 2. spar checks the `Z-User` header and sends a short-lived bind cookie with the response that
-- otherwise is the same as for `/sso/initiate-login/<idp>`
-- 3. everybody goes through the well-known SAML web sso moves, until:
-- 4. the authentication response is sent to `/sso/finalize-login/<idp>` together with the bind
-- cookie
-- 5. spar identifies the user both via the bind cookie and via the SAML authentication response,
-- and performs the binding.
--
-- Why a special cookie, and not the cookie already available, or the session token? The wire cookie
-- gets only sent to `/access`, and we would need to change that; session tokens are hard to handle
-- while switching between app context and browser context. Having a separate cookie makes both the
-- context switching simple and allows us to set a different cookie recipient end-point and short
-- cookie lifetime.
--
-- This solution is very flexible. UX variants:
--
-- * team admin posts the initiate-bind link in a group in the wire team. no new UI components
-- are needed in the frontend.
-- * we add buttons in the settings page somewhere.
-- * we send a link containing a token to the user by email. when the user clicks on the link,
-- she gets authenticated and redirected to the initiate-bind end-point in step 1 above.
-- * ...?
--
-- __Corner Case: Accidental Creation of new SSO User__
--
-- What happens if the user authenticates via SSO first, creates a new user, and then receives the
-- bind invite?
--
-- Possible solutions:
--
-- * The IdP could create all users via SCIM and we block implicit user creation.
-- * After the fact, the duplicated user deletes the SSO user, loses a little bit of user data,
-- and goes through the bind process.
-- * Block implicit creation for a short time window, and ask all existing users to use that time
-- window to bind.
type APIAuthReq =
ZOptUser
:> QueryParam "success_redirect" URI.URI
QueryParam "success_redirect" URI.URI
:> QueryParam "error_redirect" URI.URI
-- (SAML.APIAuthReq from here on, except for the cookies)
:> Capture "idp" SAML.IdPId
:> Get '[SAML.HTML] (WithSetBindCookie (SAML.FormRedirect SAML.AuthnRequest))

data DoInitiate = DoInitiateLogin | DoInitiateBind
deriving (Eq, Show, Bounded, Enum)

type WithSetBindCookie = Headers '[Servant.Header "Set-Cookie" SetBindCookie]
:> Get '[SAML.HTML] (SAML.FormRedirect SAML.AuthnRequest)

type APIAuthRespLegacy =
"finalize-login"
:> Header "Cookie" ST
-- (SAML.APIAuthResp from here on, except for response)
:> MultipartForm Mem SAML.AuthnResponseBody
:> Post '[PlainText] Void

type APIAuthResp =
"finalize-login"
:> Capture "team" TeamId
:> Header "Cookie" ST
-- (SAML.APIAuthResp from here on, except for response)
:> MultipartForm Mem SAML.AuthnResponseBody
:> Post '[PlainText] Void
Expand Down
3 changes: 1 addition & 2 deletions libs/wire-api/src/Wire/API/User/Saml.hs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ data Opts' a = Opts
instance FromJSON (Opts' (Maybe ()))

data DerivedOpts = DerivedOpts
{ derivedOptsBindCookiePath :: !SBS,
derivedOptsScimBaseURI :: !URI
{ derivedOptsScimBaseURI :: !URI
}
deriving (Show, Generic)

Expand Down
1 change: 0 additions & 1 deletion libs/wire-api/wire-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ library
Wire.API.Conversation.Protocol
Wire.API.Conversation.Role
Wire.API.Conversation.Typing
Wire.API.Cookie
Wire.API.CustomBackend
Wire.API.Error
Wire.API.Error.Brig
Expand Down
84 changes: 0 additions & 84 deletions services/brig/docs/swagger-v0.json
Original file line number Diff line number Diff line change
Expand Up @@ -11512,90 +11512,6 @@
"summary": "Change your phone number."
}
},
"/sso-initiate-bind/{idp}": {
"get": {
"parameters": [
{
"in": "query",
"name": "success_redirect",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "error_redirect",
"required": false,
"type": "string"
},
{
"format": "uuid",
"in": "path",
"name": "idp",
"required": true,
"type": "string"
}
],
"produces": [
"text/html"
],
"responses": {
"200": {
"description": "",
"headers": {
"Set-Cookie": {
"type": "string"
}
},
"schema": {
"$ref": "#/definitions/FormRedirect"
}
},
"400": {
"description": "Invalid `error_redirect` or `success_redirect`"
},
"404": {
"description": "`idp` not found"
}
}
},
"head": {
"parameters": [
{
"in": "query",
"name": "success_redirect",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "error_redirect",
"required": false,
"type": "string"
},
{
"format": "uuid",
"in": "path",
"name": "idp",
"required": true,
"type": "string"
}
],
"produces": [
"text/plain;charset=utf-8"
],
"responses": {
"200": {
"description": ""
},
"400": {
"description": "Invalid `error_redirect` or `success_redirect`"
},
"404": {
"description": "`idp` not found"
}
}
}
},
"/sso/finalize-login": {
"post": {
"parameters": [
Expand Down
84 changes: 0 additions & 84 deletions services/brig/docs/swagger-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -11512,90 +11512,6 @@
"summary": "Change your phone number."
}
},
"/sso-initiate-bind/{idp}": {
"get": {
"parameters": [
{
"in": "query",
"name": "success_redirect",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "error_redirect",
"required": false,
"type": "string"
},
{
"format": "uuid",
"in": "path",
"name": "idp",
"required": true,
"type": "string"
}
],
"produces": [
"text/html"
],
"responses": {
"200": {
"description": "",
"headers": {
"Set-Cookie": {
"type": "string"
}
},
"schema": {
"$ref": "#/definitions/FormRedirect"
}
},
"400": {
"description": "Invalid `error_redirect` or `success_redirect`"
},
"404": {
"description": "`idp` not found"
}
}
},
"head": {
"parameters": [
{
"in": "query",
"name": "success_redirect",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "error_redirect",
"required": false,
"type": "string"
},
{
"format": "uuid",
"in": "path",
"name": "idp",
"required": true,
"type": "string"
}
],
"produces": [
"text/plain;charset=utf-8"
],
"responses": {
"200": {
"description": ""
},
"400": {
"description": "Invalid `error_redirect` or `success_redirect`"
},
"404": {
"description": "`idp` not found"
}
}
}
},
"/sso/finalize-login": {
"post": {
"parameters": [
Expand Down
Loading