Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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