-
-
Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathAzureAD.hs
90 lines (82 loc) · 3 KB
/
AzureAD.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
{-# LANGUAGE QuasiQuotes #-}
-- | [AzureAD oauth2 flow](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow)
module Network.OAuth2.Provider.AzureAD where
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Except (ExceptT (..))
import Data.Aeson
import Data.ByteString.Lazy.Char8 qualified as BSL
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text.Lazy (Text)
import GHC.Generics
import Network.HTTP.Conduit (Manager)
import Network.OAuth.OAuth2
import Network.OAuth2.Experiment
import Network.OAuth2.Provider
import Network.OIDC.WellKnown
import URI.ByteString.QQ
-- Create app at https://go.microsoft.com/fwlink/?linkid=2083908
--
-- also be aware to find the right client id.
-- see https://stackoverflow.com/a/70670961
sampleAzureADAuthorizationCodeApp :: AuthorizationCodeApplication
sampleAzureADAuthorizationCodeApp =
AuthorizationCodeApplication
{ acClientId = ""
, acClientSecret = ""
, acScope = Set.fromList ["openid", "profile", "email"]
, acAuthorizeState = "CHANGE_ME"
, acAuthorizeRequestExtraParams = Map.empty
, acRedirectUri = [uri|http://localhost|]
, acName = "sample-azure-authorization-code-app"
, acTokenRequestAuthenticationMethod = ClientSecretBasic
}
fetchUserInfo ::
(MonadIO m, HasUserInfoRequest a, FromJSON b) =>
IdpApplication i a ->
Manager ->
AccessToken ->
ExceptT BSL.ByteString m b
fetchUserInfo = conduitUserInfoRequest
-- | https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
-- It's supporse to resue 'mkAzureIdp'
--
-- @
-- mkAzureIdp "common"
-- @
--
-- But its issuer is "https://login.microsoftonline.com/{tenantid}/v2.0",
-- which is invalid URI!!
defaultAzureADIdp :: Idp AzureAD
defaultAzureADIdp =
Idp
{ idpAuthorizeEndpoint = [uri|https://login.microsoftonline.com/common/oauth2/v2.0/authorize|]
, idpTokenEndpoint = [uri|https://login.microsoftonline.com/common/oauth2/v2.0/token|]
, idpUserInfoEndpoint = [uri|https://graph.microsoft.com/oidc/userinfo|]
, idpDeviceAuthorizationEndpoint = Just [uri|https://login.microsoftonline.com/common/oauth2/v2.0/devicecode|]
}
mkAzureIdp ::
MonadIO m =>
-- | Full domain with no http protocol. e.g. @contoso.onmicrosoft.com@
Text ->
ExceptT Text m (Idp AzureAD)
mkAzureIdp domain = do
OpenIDConfiguration {..} <- fetchWellKnown ("login.microsoftonline.com/" <> domain <> "/v2.0")
pure $
Idp
{ idpUserInfoEndpoint = userinfoEndpoint
, idpAuthorizeEndpoint = authorizationEndpoint
, idpTokenEndpoint = tokenEndpoint
, idpDeviceAuthorizationEndpoint = Just deviceAuthorizationEndpoint
}
-- | https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
data AzureADUser = AzureADUser
{ sub :: Text
, email :: Text
, familyName :: Text
, givenName :: Text
, name :: Text
}
deriving (Show, Generic)
instance FromJSON AzureADUser where
parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = camelTo2 '_'}