forked from okta/okta-mobile-kotlin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWebAuthentication.kt
181 lines (165 loc) · 7.63 KB
/
WebAuthentication.kt
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright 2021-Present Okta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.okta.webauthenticationui
import android.app.Activity
import android.content.Context
import androidx.annotation.VisibleForTesting
import com.okta.authfoundation.client.OAuth2Client
import com.okta.authfoundation.client.OAuth2ClientResult
import com.okta.authfoundation.client.OidcConfiguration
import com.okta.authfoundation.client.internal.SdkVersionsRegistry
import com.okta.authfoundation.credential.Token
import com.okta.oauth2.AuthorizationCodeFlow
import com.okta.oauth2.RedirectEndSessionFlow
import com.okta.webauthenticationui.events.CustomizeBrowserEvent
import com.okta.webauthenticationui.events.CustomizeCustomTabsEvent
@Deprecated(
message = "Renamed to WebAuthentication",
replaceWith = ReplaceWith("WebAuthentication")
)
typealias WebAuthenticationClient = WebAuthentication
/**
* Authentication coordinator that simplifies signing users in using browser-based OIDC authentication flows.
*
* This simple class encapsulates the details of launching the browser, and coordinating with OAuth2 endpoints.
*
* To customize the flow, please read more about customization options: [WebAuthenticationProvider], [CustomizeBrowserEvent],
* [CustomizeCustomTabsEvent].
* To further customize the authentication flow, please read more about the underlying flows: [AuthorizationCodeFlow],
* [RedirectEndSessionFlow].
*/
class WebAuthentication(
private val client: OAuth2Client,
private val webAuthenticationProvider: WebAuthenticationProvider = DefaultWebAuthenticationProvider(client.configuration.eventCoordinator),
) {
companion object {
init {
SdkVersionsRegistry.register(SDK_VERSION)
}
}
/**
* Initializes a web authentication client.
*
* @param webAuthenticationProvider the [WebAuthenticationProvider] which will be used to show the UI when performing the
* redirect flows.
*/
constructor(
webAuthenticationProvider: WebAuthenticationProvider = DefaultWebAuthenticationProvider(OidcConfiguration.default.eventCoordinator)
) : this(OAuth2Client.default, webAuthenticationProvider)
/**
* Initializes a web authentication client.
*
* @param oidcConfiguration the [OidcConfiguration] specifying the authorization servers.
* @param webAuthenticationProvider the [WebAuthenticationProvider] which will be used to show the UI when performing the
* redirect flows.
*/
constructor(
oidcConfiguration: OidcConfiguration,
webAuthenticationProvider: WebAuthenticationProvider = DefaultWebAuthenticationProvider(oidcConfiguration.eventCoordinator)
) : this(OAuth2Client.createFromConfiguration(oidcConfiguration), webAuthenticationProvider)
var authorizationCodeFlow: AuthorizationCodeFlow = AuthorizationCodeFlow(client)
var redirectEndSessionFlow: RedirectEndSessionFlow = RedirectEndSessionFlow(client)
@VisibleForTesting internal var redirectCoordinator: RedirectCoordinator = SingletonRedirectCoordinator
/**
* Used in a [OAuth2ClientResult.Error.exception].
*
* Indicates the requested flow was cancelled.
*/
class FlowCancelledException internal constructor() : Exception("Flow cancelled.")
/**
* Initiates the OIDC Authorization Code redirect flow.
*
* @param context the Android [Activity] [Context] which is used to display the login flow via the configured
* [WebAuthenticationProvider].
* @param redirectUrl the redirect URL.
* @param extraRequestParameters the extra key value pairs to send to the authorize endpoint.
* See [Authorize Documentation](https://developer.okta.com/docs/reference/api/oidc/#authorize) for parameter options.
* @param scope the scopes to request during sign in. Defaults to the configured [client] [OidcConfiguration.defaultScope].
*/
suspend fun login(
context: Context,
redirectUrl: String,
extraRequestParameters: Map<String, String> = emptyMap(),
scope: String = client.configuration.defaultScope,
): OAuth2ClientResult<Token> {
val initializationResult = redirectCoordinator.initialize(webAuthenticationProvider, context) {
when (val result = authorizationCodeFlow.start(redirectUrl, extraRequestParameters, scope)) {
is OAuth2ClientResult.Success -> {
RedirectInitializationResult.Success(result.result.url, result.result)
}
is OAuth2ClientResult.Error -> {
RedirectInitializationResult.Error(result.exception)
}
}
}
val flowContext = when (initializationResult) {
is RedirectInitializationResult.Error -> {
return OAuth2ClientResult.Error(initializationResult.exception)
}
is RedirectInitializationResult.Success -> {
initializationResult.flowContext
}
}
val uri = when (val redirectResult = redirectCoordinator.listenForResult()) {
is RedirectResult.Error -> {
return OAuth2ClientResult.Error(redirectResult.exception)
}
is RedirectResult.Redirect -> {
redirectResult.uri
}
}
return authorizationCodeFlow.resume(uri, flowContext)
}
/**
* Initiates the OIDC logout redirect flow.
*
* > Note: OIDC Logout terminology is nuanced, see [Logout Documentation](https://github.com/okta/okta-mobile-kotlin#logout) for additional details.
*
* @param context the Android [Activity] [Context] which is used to display the logout flow via the configured
* [WebAuthenticationProvider].
* @param redirectUrl the redirect URL.
* @param idToken the token used to identify the session to log the user out of.
*/
suspend fun logoutOfBrowser(context: Context, redirectUrl: String, idToken: String): OAuth2ClientResult<Unit> {
val initializationResult = redirectCoordinator.initialize(webAuthenticationProvider, context) {
when (val result = redirectEndSessionFlow.start(redirectUrl, idToken)) {
is OAuth2ClientResult.Success -> {
RedirectInitializationResult.Success(result.result.url, result.result)
}
is OAuth2ClientResult.Error -> {
RedirectInitializationResult.Error(result.exception)
}
}
}
val flowContext = when (initializationResult) {
is RedirectInitializationResult.Error -> {
return OAuth2ClientResult.Error(initializationResult.exception)
}
is RedirectInitializationResult.Success -> {
initializationResult.flowContext
}
}
val uri = when (val redirectResult = redirectCoordinator.listenForResult()) {
is RedirectResult.Error -> {
return OAuth2ClientResult.Error(redirectResult.exception)
}
is RedirectResult.Redirect -> {
redirectResult.uri
}
}
return redirectEndSessionFlow.resume(uri, flowContext)
}
}