14
14
*/
15
15
package com.amazonaws.amplify.amplify_api.auth
16
16
17
+ import android.os.Looper
17
18
import com.amplifyframework.api.ApiException
18
19
import com.amplifyframework.api.aws.ApiAuthProviders
19
20
import com.amplifyframework.api.aws.AuthorizationType
20
21
import com.amplifyframework.api.aws.sigv4.FunctionAuthProvider
21
22
import com.amplifyframework.api.aws.sigv4.OidcAuthProvider
23
+ import io.flutter.Log
24
+ import io.flutter.plugin.common.MethodChannel
25
+ import kotlinx.coroutines.*
22
26
23
27
/* *
24
28
* Manages the shared state of all [FlutterAuthProvider] instances.
25
29
*/
26
- object FlutterAuthProviders {
30
+ class FlutterAuthProviders (private val methodChannel : MethodChannel ) {
31
+
32
+ private companion object {
33
+ /* *
34
+ * Timeout on a single [getToken] call.
35
+ */
36
+ const val getTokenTimeoutMillis = 2000L
37
+
38
+ /* *
39
+ * Logger tag.
40
+ */
41
+ const val tag = " FlutterAuthProviders"
42
+
43
+ /* *
44
+ * Name for suspending block in [getToken]. Used for debugging
45
+ */
46
+ val coroutineName = CoroutineName (tag)
47
+ }
48
+
27
49
/* *
28
50
* A factory of [FlutterAuthProvider] instances.
29
51
*/
30
52
val factory: ApiAuthProviders by lazy {
31
53
ApiAuthProviders
32
54
.Builder ()
33
- .functionAuthProvider(FlutterAuthProvider (AuthorizationType .AWS_LAMBDA ))
34
- .oidcAuthProvider(FlutterAuthProvider (AuthorizationType .OPENID_CONNECT ))
55
+ .functionAuthProvider(FlutterAuthProvider (this , AuthorizationType .AWS_LAMBDA ))
56
+ .oidcAuthProvider(FlutterAuthProvider (this , AuthorizationType .OPENID_CONNECT ))
35
57
.build()
36
58
}
37
59
38
- /* *
39
- * Token cache for all [FlutterAuthProvider] instances.
40
- */
41
- private var tokens: MutableMap <AuthorizationType , String ?> = mutableMapOf ()
42
-
43
60
/* *
44
61
* Retrieves the token for [authType] or `null`, if unavailable.
62
+ *
63
+ * This function is typically called from within the Amplify library and from a thread besides
64
+ * the main one, where it is safe to block. In API REST, the calling thread is main and we must
65
+ * return `null`.
66
+ *
67
+ * Not blocking the main thread is required for making platform channel calls without deadlock.
45
68
*/
46
- fun getToken (authType : AuthorizationType ): String? = tokens[authType]
69
+ fun getToken (authType : AuthorizationType ): String? {
70
+ if (Thread .currentThread() == Looper .getMainLooper().thread) {
71
+ // API REST will call this function from the main thread on configuration. This is bad.
72
+ // Since we have to block the calling thread to retrieve the token, just return null.
73
+ Log .e(tag, " REST OIDC/Lambda is not supported yet." )
74
+ return null
75
+ }
76
+ try {
77
+ return runBlocking(coroutineName) {
78
+ val completer = Job ()
47
79
48
- /* *
49
- * Sets the token for [authType] to [value].
50
- */
51
- fun setToken (authType : AuthorizationType , value : String? ) {
52
- tokens[authType] = value
80
+ val result = object : MethodChannel .Result {
81
+ var token: String? = null
82
+
83
+ override fun success (result : Any? ) {
84
+ token = result as ? String
85
+ launch(Dispatchers .Main ) {
86
+ completer.complete()
87
+ }
88
+ }
89
+
90
+ override fun error (
91
+ errorCode : String? ,
92
+ errorMessage : String? ,
93
+ errorDetails : Any?
94
+ ) {
95
+ launch(Dispatchers .Main ) {
96
+ completer.complete()
97
+ }
98
+ }
99
+
100
+ override fun notImplemented () {
101
+ launch(Dispatchers .Main ) {
102
+ completer.complete()
103
+ }
104
+ }
105
+ }
106
+ launch(Dispatchers .Main ) {
107
+ methodChannel.invokeMethod(
108
+ " getLatestAuthToken" ,
109
+ authType.name,
110
+ result
111
+ )
112
+ }
113
+
114
+ withTimeout(getTokenTimeoutMillis) {
115
+ completer.join()
116
+ }
117
+
118
+ return @runBlocking result.token
119
+ }
120
+ } catch (e: Exception ) {
121
+ Log .e(tag, " Exception in getToken" , e)
122
+ return null
123
+ }
53
124
}
54
125
}
55
126
56
127
/* *
57
128
* A provider which manages token retrieval for its [AuthorizationType].
58
129
*/
59
- class FlutterAuthProvider (private val type : AuthorizationType ) : FunctionAuthProvider,
130
+ class FlutterAuthProvider (
131
+ private val provider : FlutterAuthProviders ,
132
+ private val type : AuthorizationType
133
+ ) : FunctionAuthProvider,
60
134
OidcAuthProvider {
61
135
private companion object {
62
136
/* *
63
137
* Thrown when there is no token available for [type].
64
138
*/
65
139
fun noTokenAvailable (type : AuthorizationType ) = ApiException .ApiAuthException (
66
- " No $type token available" ,
67
- " Ensure that `getLatestAuthToken` returns a value"
140
+ " Unable to retrieve token for $type " ,
141
+ """
142
+ Make sure you register your auth providers in the addPlugin call and
143
+ that getLatestAuthToken returns a value.
144
+ """ .trimIndent()
68
145
)
69
146
}
70
147
71
148
override fun getLatestAuthToken (): String =
72
- FlutterAuthProviders .getToken(type) ? : throw noTokenAvailable(type)
149
+ provider .getToken(type) ? : throw noTokenAvailable(type)
73
150
}
0 commit comments