Skip to content

Commit 9a6ac54

Browse files
authored
Sqservices 1157 backend 2nd auth factor for create authentication token for SCIM service (#2149)
1 parent 2013586 commit 9a6ac54

File tree

37 files changed

+319
-82
lines changed

37 files changed

+319
-82
lines changed

changelog.d/0-release-notes/pr-2149

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deploy Brig before Spar.

changelog.d/2-features/pr-2149

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2nd factor authentication for "create SCIM token" via 6 digit code, sent by email. The feature is disabled per default and can be enabled server or team wide. For details see [/docs/reference/config-options.md#2nd-factor-password-challenge](https://github.com/wireapp/wire-server/blob/develop/docs/reference/config-options.md#2nd-factor-password-challenge)

changelog.d/5-internal/pr-2149

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Internal endpoint for re-authentication (`GET "/i/users/:uid/reauthenticate"`) in brig has changed in a backwards compatible way. Spar depends on this change for creating a SCIM token with 2nd password challenge.

libs/brig-types/src/Brig/Types/Intra.hs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ import Brig.Types.Connection
3434
import Brig.Types.User
3535
import Data.Aeson
3636
import qualified Data.Aeson.KeyMap as KeyMap
37+
import Data.Code as Code
3738
import Data.Id (TeamId, UserId)
3839
import Data.Misc (PlainTextPassword (..))
3940
import qualified Data.Text as Text
4041
import Imports
42+
import Wire.API.User (VerificationAction (..))
4143

4244
-------------------------------------------------------------------------------
4345
-- AccountStatus
@@ -169,16 +171,21 @@ instance ToJSON UserSet where
169171

170172
-- | Certain operations might require reauth of the user. These are available
171173
-- only for users that have already set a password.
172-
newtype ReAuthUser = ReAuthUser
173-
{reAuthPassword :: Maybe PlainTextPassword}
174+
data ReAuthUser = ReAuthUser
175+
{ reAuthPassword :: Maybe PlainTextPassword,
176+
reAuthCode :: Maybe Code.Value,
177+
reAuthCodeAction :: Maybe VerificationAction
178+
}
174179
deriving (Eq, Show, Generic)
175180

176181
instance FromJSON ReAuthUser where
177182
parseJSON = withObject "reauth-user" $ \o ->
178-
ReAuthUser <$> o .:? "password"
183+
ReAuthUser <$> o .:? "password" <*> o .:? "verification_code" <*> o .:? "action"
179184

180185
instance ToJSON ReAuthUser where
181186
toJSON ru =
182187
object
183-
[ "password" .= reAuthPassword ru
188+
[ "password" .= reAuthPassword ru,
189+
"verification_code" .= reAuthCode ru,
190+
"action" .= reAuthCodeAction ru
184191
]

libs/brig-types/test/unit/Test/Brig/Types/User.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ instance Arbitrary RichInfoUpdate where
5757
arbitrary = RichInfoUpdate <$> arbitrary
5858

5959
instance Arbitrary ReAuthUser where
60-
arbitrary = ReAuthUser <$> arbitrary
60+
arbitrary = ReAuthUser <$> arbitrary <*> arbitrary <*> arbitrary
6161

6262
instance Arbitrary NewUserScimInvitation where
6363
arbitrary = NewUserScimInvitation <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary

libs/wire-api/src/Wire/API/ErrorDescription.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ type PasswordAuthenticationFailed = ErrorDescription 403 "password-authenticatio
379379

380380
type CodeAuthenticationFailed = ErrorDescription 403 "code-authentication-failed" "Code authentication failed."
381381

382+
type CodeAuthenticationRequired = ErrorDescription 403 "code-authentication-required" "Code authentication is required."
383+
382384
type MLSProtocolError = ErrorDescription 400 "mls-protocol-error" "MLS protocol error"
383385

384386
mlsProtocolError :: Text -> MLSProtocolError

libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module Wire.API.Routes.Internal.Brig
3131
where
3232

3333
import Control.Lens ((.~))
34+
import qualified Data.Code as Code
3435
import Data.Id as Id
3536
import Data.Swagger (HasInfo (info), HasTitle (title), Swagger)
3637
import Imports hiding (head)
@@ -152,9 +153,17 @@ type GetClientByKeyPackageRef =
152153
]
153154
(Maybe ClientIdentity)
154155

156+
type GetVerificationCode =
157+
Summary "Get verification code for a given email and action"
158+
:> "users"
159+
:> Capture "uid" UserId
160+
:> "verification-code"
161+
:> Capture "action" VerificationAction
162+
:> Get '[Servant.JSON] (Maybe Code.Value)
163+
155164
type API =
156165
"i"
157-
:> (EJPD_API :<|> AccountAPI :<|> MLSAPI)
166+
:> (EJPD_API :<|> AccountAPI :<|> MLSAPI :<|> GetVerificationCode)
158167

159168
type SwaggerDocsAPI = "api" :> "internal" :> SwaggerSchemaUI "swagger-ui" "swagger.json"
160169

libs/wire-api/src/Wire/API/Routes/Public/Spar.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import Web.Scim.Capabilities.MetaSchema as Scim.Meta
3333
import Web.Scim.Class.Auth as Scim.Auth
3434
import Web.Scim.Class.User as Scim.User
3535
import Wire.API.Cookie
36-
import Wire.API.ErrorDescription (CanThrow, CodeAuthenticationFailed, PasswordAuthenticationFailed)
36+
import Wire.API.ErrorDescription
3737
import Wire.API.Routes.Public
3838
import Wire.API.User.IdentityProvider
3939
import Wire.API.User.Saml
@@ -207,6 +207,7 @@ type APIScim =
207207
:<|> "auth-tokens"
208208
:> CanThrow PasswordAuthenticationFailed
209209
:> CanThrow CodeAuthenticationFailed
210+
:> CanThrow CodeAuthenticationRequired
210211
:> APIScimToken
211212

212213
type ScimSiteAPI tag = ToServantApi (ScimSite tag)

libs/wire-api/src/Wire/API/User.hs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
{-# LANGUAGE DeriveGeneric #-}
12
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23
{-# LANGUAGE RecordWildCards #-}
3-
{-# LANGUAGE StrictData #-}
44

55
-- This file is part of the Wire Server implementation.
66
--
@@ -109,11 +109,13 @@ import Control.Error.Safe (rightMay)
109109
import Control.Lens (over, (.~), (?~))
110110
import Data.Aeson (FromJSON (..), ToJSON (..))
111111
import qualified Data.Aeson.Types as A
112+
import qualified Data.Attoparsec.ByteString as Parser
112113
import Data.ByteString.Conversion
113114
import qualified Data.CaseInsensitive as CI
114115
import qualified Data.Code as Code
115116
import qualified Data.Currency as Currency
116117
import Data.Domain (Domain (Domain))
118+
import Data.Either.Extra (maybeToEither)
117119
import Data.Handle (Handle)
118120
import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
119121
import Data.Id
@@ -124,17 +126,20 @@ import Data.Qualified
124126
import Data.Range
125127
import Data.SOP
126128
import Data.Schema
129+
import Data.String.Conversions (cs)
127130
import qualified Data.Swagger as S
128131
import qualified Data.Swagger.Build.Api as Doc
132+
import qualified Data.Text as T
129133
import Data.Text.Ascii
134+
import qualified Data.Text.Encoding as T
130135
import Data.UUID (UUID, nil)
131136
import qualified Data.UUID as UUID
132137
import Deriving.Swagger
133138
import GHC.TypeLits (KnownNat, Nat)
134139
import qualified Generics.SOP as GSOP
135140
import Imports
136141
import qualified SAML2.WebSSO as SAML
137-
import Servant (type (.++))
142+
import Servant (FromHttpApiData (..), ToHttpApiData (..), type (.++))
138143
import qualified Test.QuickCheck as QC
139144
import qualified Web.Cookie as Web
140145
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..))
@@ -1195,7 +1200,7 @@ instance S.ToSchema ListUsersQuery where
11951200
-----------------------------------------------------------------------------
11961201
-- SndFactorPasswordChallenge
11971202

1198-
data VerificationAction = GenerateScimToken | Login
1203+
data VerificationAction = CreateScimToken | Login
11991204
deriving stock (Eq, Show, Enum, Bounded, Generic)
12001205
deriving (Arbitrary) via (GenericUniform VerificationAction)
12011206
deriving (FromJSON, ToJSON, S.ToSchema) via (Schema VerificationAction)
@@ -1204,10 +1209,36 @@ instance ToSchema VerificationAction where
12041209
schema =
12051210
enum @Text "VerificationAction" $
12061211
mconcat
1207-
[ element "generate_scim_token" GenerateScimToken,
1212+
[ element "create_scim_token" CreateScimToken,
12081213
element "login" Login
12091214
]
12101215

1216+
instance ToByteString VerificationAction where
1217+
builder CreateScimToken = "create_scim_token"
1218+
builder Login = "login"
1219+
1220+
instance FromByteString VerificationAction where
1221+
parser =
1222+
Parser.takeByteString >>= \b ->
1223+
case T.decodeUtf8' b of
1224+
Right "login" -> pure Login
1225+
Right "create_scim_token" -> pure CreateScimToken
1226+
Right t -> fail $ "Invalid VerificationAction: " <> T.unpack t
1227+
Left e -> fail $ "Invalid VerificationAction: " <> show e
1228+
1229+
instance S.ToParamSchema VerificationAction where
1230+
toParamSchema _ =
1231+
mempty
1232+
{ S._paramSchemaType = Just S.SwaggerString,
1233+
S._paramSchemaEnum = Just (A.String . toQueryParam <$> [(minBound :: VerificationAction) ..])
1234+
}
1235+
1236+
instance FromHttpApiData VerificationAction where
1237+
parseUrlPiece = maybeToEither "Invalid verification action" . fromByteString . cs
1238+
1239+
instance ToHttpApiData VerificationAction where
1240+
toQueryParam a = cs (toByteString' a)
1241+
12111242
data SendVerificationCode = SendVerificationCode
12121243
{ svcAction :: VerificationAction,
12131244
svcEmail :: Email

libs/wire-api/src/Wire/API/User/Scim.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import qualified Data.Binary.Builder as BB
5454
import Data.ByteArray.Encoding (Base (..), convertToBase)
5555
import Data.ByteString.Conversion (FromByteString (..), ToByteString (..))
5656
import qualified Data.CaseInsensitive as CI
57+
import Data.Code as Code
5758
import Data.Handle (Handle)
5859
import Data.Id (ScimTokenId, TeamId, UserId)
5960
import Data.Json.Util ((#))
@@ -84,7 +85,6 @@ import qualified Web.Scim.Schema.Schema as Scim
8485
import qualified Web.Scim.Schema.User as Scim
8586
import qualified Web.Scim.Schema.User as Scim.User
8687
import Wire.API.Arbitrary (Arbitrary, GenericUniform (..))
87-
import Wire.API.User.Activation
8888
import Wire.API.User.Identity (Email)
8989
import Wire.API.User.Profile as BT
9090
import qualified Wire.API.User.RichInfo as RI
@@ -369,7 +369,7 @@ data CreateScimToken = CreateScimToken
369369
-- | User password, which we ask for because creating a token is a "powerful" operation
370370
createScimTokenPassword :: !(Maybe PlainTextPassword),
371371
-- | User code (sent by email), for 2nd factor to 'createScimTokenPassword'
372-
createScimTokenCode :: !(Maybe ActivationCode)
372+
createScimTokenCode :: !(Maybe Code.Value)
373373
}
374374
deriving (Eq, Show, Generic)
375375
deriving (Arbitrary) via (GenericUniform CreateScimToken)

0 commit comments

Comments
 (0)