-
Notifications
You must be signed in to change notification settings - Fork 0
/
Graph.fs
128 lines (109 loc) · 4.97 KB
/
Graph.fs
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
module Graph
open System.Net.Http
open System.Net.Http.Headers
open System.Threading.Tasks
open System.Security.Claims
open Microsoft.Graph
open Microsoft.Extensions.Configuration
open Microsoft.Identity.Client
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.DependencyInjection
open FSharp.Control.Tasks.V2
open AzureAdOptions
type Uri = System.Uri
type StringSplitOptions = System.StringSplitOptions
type IGraphAuthProvider =
abstract GetUserAccessTokenAsync : string -> Task<string>
abstract GetUserAccessTokenByAuthorizationCode : string -> Task<AuthenticationResult>
type GraphAuthProvider(configuration : IConfiguration) =
let azureOptions = AzureAdOptions()
do configuration.Bind("AzureAd", azureOptions)
let authority = sprintf "%s/%s" azureOptions.Instance azureOptions.TenantId
let _app =
ConfidentialClientApplicationBuilder
.Create(azureOptions.ClientId)
.WithClientSecret(azureOptions.ClientSecret)
.WithAuthority(Uri(authority))
// .WithAuthority(AzureCloudInstance.AzurePublic, AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
.WithRedirectUri(azureOptions.BaseUrl + azureOptions.CallbackPath)
.Build()
let _scopes = azureOptions.GraphScopes.Split(' ' , StringSplitOptions.RemoveEmptyEntries)
// More info about MSAL Client Applications: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-Applications
let serviceErrorExn code msg =
let e = Error()
e.Code <- code
e.Message <- msg
raise (ServiceException(e))
let getUserAccessTokenAsync (userId : string) =
task {
// let! account = _app.GetAccountAsync userId
// if isNull account then
// return serviceErrorExn
// "TokenNotFound"
// "User not found in token cache. Maybe the server was restarted."
// else
try
let! result =
// _app.AcquireTokenSilent(_scopes, account)
_app.AcquireTokenForClient(_scopes)
.ExecuteAsync()
return result.AccessToken
with _ ->
return serviceErrorExn
(GraphErrorCode.AuthenticationFailure |> string)
"Caller needs to authenticate. Unable to retrieve the access token silently."
}
let getUserAccessTokenByAuthorizationCode (authorizationCode : string) =
task {
return!
_app.AcquireTokenByAuthorizationCode(_scopes, authorizationCode)
.ExecuteAsync()
}
interface IGraphAuthProvider with
// Gets an access token. First tries to get the access token from the token cache.
// Using password (secret) to authenticate. Production apps should use a certificate.
member this.GetUserAccessTokenAsync (userId : string) =
getUserAccessTokenAsync userId
member this.GetUserAccessTokenByAuthorizationCode (authCode : string) =
getUserAccessTokenByAuthorizationCode authCode
let private getIdent (userIdentity : ClaimsIdentity) (t : string) =
match userIdentity.FindFirst t with
| null -> ""
| x -> x.Value
let graphAuthToken (userIdentity : ClaimsIdentity) (ctx : HttpContext) =
let authProvider = ctx.RequestServices.GetService<IGraphAuthProvider>()
let id1 = getIdent userIdentity objectIdentifierType
let id2 = getIdent userIdentity tenantIdType
let identifier = id1 + "." + id2
task {
let! accessToken = authProvider.GetUserAccessTokenAsync identifier
let authHeader = AuthenticationHeaderValue("Bearer", accessToken)
return authHeader.ToString ()
}
let graphAppToken (ctx : HttpContext) =
let authProvider = ctx.RequestServices.GetService<IGraphAuthProvider>()
task {
let! accessToken = authProvider.GetUserAccessTokenAsync ""
let authHeader = AuthenticationHeaderValue("Bearer", accessToken)
return authHeader.ToString ()
}
let graphAuthProvider
(authProvider : IGraphAuthProvider)
(userIdentity : ClaimsIdentity)
(requestMessage : HttpRequestMessage) =
// Get user's id for token cache.
let id1 = getIdent userIdentity objectIdentifierType
let id2 = getIdent userIdentity tenantIdType
let identifier = id1 + "." + id2
task {
// Passing tenant ID to the sample auth provider to use as a cache key
let! accessToken = authProvider.GetUserAccessTokenAsync identifier
// Append the access token to the request
requestMessage.Headers.Authorization <-
AuthenticationHeaderValue("Bearer", accessToken)
// requestMessage.Headers.Add("SampleID", "aspnetcore-connect-sample")
}
let graphUrl x = "https://graph.microsoft.com/v1.0/" + x
let inline graphUrlf (fmt : Printf.StringFormat<_, _>) =
let f = Printf.StringFormat<_, _>("https://graph.microsoft.com/v1.0/" + fmt.Value)
sprintf f