-
Notifications
You must be signed in to change notification settings - Fork 125
/
OAuthHelper.kt
95 lines (85 loc) · 4.63 KB
/
OAuthHelper.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
package net.dean.jraw.oauth
import net.dean.jraw.RedditClient
import net.dean.jraw.http.HttpRequest
import net.dean.jraw.http.NetworkAdapter
import net.dean.jraw.http.NetworkException
import net.dean.jraw.models.OAuthData
import net.dean.jraw.models.internal.OAuthDataJson
import java.util.*
/**
* This class helps create authenticated RedditClient instances.
*
* There are two main types of authentication: automatic and interactive. Automatic authentication can be done without
* asking the user, while interactive authentication requires the user to log in to their account and authorize your
* reddit OAuth2 app to access their account.
*
* Script apps and those using application-only (userless) mode are the only apps that qualify for automatic
* authentication.
*/
object OAuthHelper {
/**
* Authenticates a client without user approval. This is only applicable to scripts apps and those using userless
* mode. This is possible because reddit implicitly grants access to the app to the account that created the script,
* and userless mode by definition has no user to authenticate. The NetworkAdapter, Credentials, and TokenStore are
* eventually given to the RedditClient.
*/
@JvmStatic @JvmOverloads fun automatic(http: NetworkAdapter, creds: Credentials, tokenStore: TokenStore = NoopTokenStore()): RedditClient {
return when {
creds.authMethod.isUserless ->
RedditClient(http, applicationOnlyOAuthData(http, creds), creds, tokenStore, overrideUsername = AuthManager.USERNAME_USERLESS)
creds.authMethod == AuthMethod.SCRIPT ->
RedditClient(http, scriptOAuthData(http, creds), creds, tokenStore, overrideUsername = creds.username)
else -> throw IllegalArgumentException("AuthMethod ${creds.authMethod} is not eligible for automatic authentication")
}
}
/**
* Starts the authentication process for an installed or web app. Like with [automatic], the NetworkAdapter,
* Credentials, and TokenStore are eventually given to the RedditClient after authentication.
*/
@JvmStatic @JvmOverloads fun interactive(http: NetworkAdapter, creds: Credentials, tokenStore: TokenStore = NoopTokenStore()) =
interactive(http, creds, tokenStore, onAuthenticated = {})
@JvmStatic internal fun interactive(http: NetworkAdapter, creds: Credentials,
tokenStore: TokenStore = NoopTokenStore(),
onAuthenticated: (r: RedditClient) -> Unit = {}): StatefulAuthHelper {
return when (creds.authMethod) {
AuthMethod.APP -> StatefulAuthHelper(http, creds, tokenStore, onAuthenticated)
AuthMethod.WEBAPP -> TODO("Web apps aren't supported yet")
else -> throw IllegalArgumentException("AuthMethod ${creds.authMethod} should use automatic authentication")
}
}
@JvmStatic internal fun scriptOAuthData(http: NetworkAdapter, creds: Credentials): OAuthData {
if (creds.authMethod != AuthMethod.SCRIPT)
throw IllegalArgumentException("This function is for script apps only")
try {
return http.execute(HttpRequest.Builder()
.post(mapOf(
"grant_type" to "password",
"username" to creds.username!!,
"password" to creds.password!!
))
.url("https://www.reddit.com/api/v1/access_token")
.basicAuth(creds.clientId to creds.clientSecret)
.build()).deserialize<OAuthDataJson>().toOAuthData()
} catch (e: NetworkException) {
if (e.res.code == 401)
throw IllegalArgumentException("Invalid credentials", e)
throw e
}
}
@JvmStatic internal fun applicationOnlyOAuthData(http: NetworkAdapter, creds: Credentials): OAuthData {
if (!creds.authMethod.isUserless)
throw IllegalArgumentException("${creds.authMethod} is not a userless authentication method")
val grantType = if (creds.authMethod == AuthMethod.USERLESS_APP)
"https://oauth.reddit.com/grants/installed_client"
else
"client_credentials"
val postBody = mutableMapOf("grant_type" to grantType)
if (creds.authMethod == AuthMethod.USERLESS_APP)
postBody.put("device_id", creds.deviceId.toString())
return http.execute(HttpRequest.Builder()
.url("https://www.reddit.com/api/v1/access_token")
.post(postBody)
.basicAuth(creds.clientId to creds.clientSecret)
.build()).deserialize<OAuthDataJson>().toOAuthData()
}
}