@@ -6,21 +6,31 @@ defmodule Ueberauth.Strategy.Google do
6
6
use Ueberauth.Strategy ,
7
7
uid_field: :sub ,
8
8
default_scope: "email" ,
9
- hd: nil ,
10
- userinfo_endpoint: "https://www.googleapis.com/oauth2/v3/userinfo"
9
+ hd: nil
11
10
12
- alias Ueberauth.Auth.Info
13
- alias Ueberauth.Auth.Credentials
14
11
alias Ueberauth.Auth.Extra
15
12
13
+ @ session_key "ueberauth_strategy_google"
14
+
16
15
@ doc """
17
16
Handles initial request for Google authentication.
18
17
"""
19
18
def handle_request! ( conn ) do
20
- scopes = conn . params [ "scope" ] || option ( conn , :default_scope )
21
-
22
- params =
23
- [ scope: scopes ]
19
+ scopes =
20
+ String . split (
21
+ conn . params [ "scope" ] || option ( conn , :default_scope ) ,
22
+ " "
23
+ )
24
+
25
+ scopes =
26
+ if "openid" in scopes do
27
+ scopes
28
+ else
29
+ [ "openid" ] ++ scopes
30
+ end
31
+
32
+ authorization_params =
33
+ [ ]
24
34
|> with_optional ( :hd , conn )
25
35
|> with_optional ( :prompt , conn )
26
36
|> with_optional ( :access_type , conn )
@@ -30,33 +40,39 @@ defmodule Ueberauth.Strategy.Google do
30
40
|> with_param ( :prompt , conn )
31
41
|> with_param ( :login_hint , conn )
32
42
|> with_param ( :hl , conn )
33
- |> with_state_param ( conn )
34
43
35
- opts = oauth_client_options_from_conn ( conn )
36
- redirect! ( conn , Ueberauth.Strategy.Google.OAuth . authorize_url! ( params , opts ) )
44
+ opts =
45
+ conn
46
+ |> options_from_conn ( )
47
+ |> Map . put ( :scopes , scopes )
48
+ |> Map . put ( :authorization_params , Map . new ( authorization_params ) )
49
+
50
+ case UeberauthOidcc.Request . handle_request ( opts , conn ) do
51
+ { :ok , conn } ->
52
+ conn
53
+
54
+ { :error , conn , reason } ->
55
+ UeberauthOidcc.Error . set_described_error ( conn , reason , "error" )
56
+ end
37
57
end
38
58
39
59
@ doc """
40
60
Handles the callback from Google.
41
61
"""
42
- def handle_callback! ( % Plug.Conn { params: % { "code" => code } } = conn ) do
43
- params = [ code: code ]
44
- opts = oauth_client_options_from_conn ( conn )
62
+ def handle_callback! ( % Plug.Conn { } = conn ) do
63
+ opts = options_from_conn ( conn )
45
64
46
- case Ueberauth.Strategy.Google.OAuth . get_access_token ( params , opts ) do
47
- { :ok , token } ->
48
- fetch_user ( conn , token )
65
+ case UeberauthOidcc.Callback . handle_callback ( opts , conn ) do
66
+ { :ok , conn , token , userinfo } ->
67
+ conn
68
+ |> put_private ( :google_token , token )
69
+ |> put_private ( :google_user , userinfo )
49
70
50
- { :error , { error_code , error_description } } ->
51
- set_errors! ( conn , [ error ( error_code , error_description ) ] )
71
+ { :error , conn , reason } ->
72
+ UeberauthOidcc.Error . set_described_error ( conn , reason , "error" )
52
73
end
53
74
end
54
75
55
- @ doc false
56
- def handle_callback! ( conn ) do
57
- set_errors! ( conn , [ error ( "missing_code" , "No code received" ) ] )
58
- end
59
-
60
76
@ doc false
61
77
def handle_cleanup! ( conn ) do
62
78
conn
@@ -81,87 +97,49 @@ defmodule Ueberauth.Strategy.Google do
81
97
"""
82
98
def credentials ( conn ) do
83
99
token = conn . private . google_token
84
- scope_string = token . other_params [ "scope" ] || ""
85
- scopes = String . split ( scope_string , " " )
86
-
87
- % Credentials {
88
- expires: ! ! token . expires_at ,
89
- expires_at: token . expires_at ,
90
- scopes: scopes ,
91
- token_type: Map . get ( token , :token_type ) ,
92
- refresh_token: token . refresh_token ,
93
- token: token . access_token
94
- }
100
+ credentials = UeberauthOidcc.Auth . credentials ( token )
101
+ % { credentials | other: % { } }
95
102
end
96
103
97
104
@ doc """
98
105
Fetches the fields to populate the info section of the `Ueberauth.Auth` struct.
99
106
"""
100
107
def info ( conn ) do
108
+ token = conn . private . google_token
101
109
user = conn . private . google_user
102
110
103
- % Info {
104
- email: user [ "email" ] ,
105
- first_name: user [ "given_name" ] ,
106
- image: user [ "picture" ] ,
107
- last_name: user [ "family_name" ] ,
108
- name: user [ "name" ] ,
109
- birthday: user [ "birthday" ] ,
110
- urls: % {
111
- profile: user [ "profile" ] ,
112
- website: user [ "hd" ]
113
- }
111
+ info = UeberauthOidcc.Auth . info ( token , user )
112
+
113
+ % {
114
+ info
115
+ | birthday: info . birthday || user [ "birthday" ] ,
116
+ urls: Map . put_new ( info . urls , :website , user [ "hd" ] )
114
117
}
115
118
end
116
119
117
120
@ doc """
118
121
Stores the raw information (including the token) obtained from the google callback.
119
122
"""
120
123
def extra ( conn ) do
124
+ creds = credentials ( conn )
125
+
126
+ # create a struct with the same format as the old token, even if we don't depend on OAuth2
127
+ google_token = % {
128
+ __struct__: OAuth2.AccessToken ,
129
+ access_token: creds . token ,
130
+ refresh_token: creds . refresh_token ,
131
+ expires_at: creds . expires_at ,
132
+ token_type: "Bearer"
133
+ }
134
+
121
135
% Extra {
122
136
raw_info: % {
123
- token: conn . private . google_token ,
137
+ token: google_token ,
124
138
user: conn . private . google_user
125
139
}
126
140
}
127
141
end
128
142
129
- defp fetch_user ( conn , token ) do
130
- conn = put_private ( conn , :google_token , token )
131
-
132
- # userinfo_endpoint from https://accounts.google.com/.well-known/openid-configuration
133
- # the userinfo_endpoint may be overridden in options when necessary.
134
- resp = Ueberauth.Strategy.Google.OAuth . get ( token , get_userinfo_endpoint ( conn ) )
135
-
136
- case resp do
137
- { :ok , % OAuth2.Response { status_code: 401 , body: _body } } ->
138
- set_errors! ( conn , [ error ( "token" , "unauthorized" ) ] )
139
-
140
- { :ok , % OAuth2.Response { status_code: status_code , body: user } }
141
- when status_code in 200 .. 399 ->
142
- put_private ( conn , :google_user , user )
143
-
144
- { :error , % OAuth2.Response { status_code: status_code } } ->
145
- set_errors! ( conn , [ error ( "OAuth2" , status_code ) ] )
146
-
147
- { :error , % OAuth2.Error { reason: reason } } ->
148
- set_errors! ( conn , [ error ( "OAuth2" , reason ) ] )
149
- end
150
- end
151
-
152
- defp get_userinfo_endpoint ( conn ) do
153
- case option ( conn , :userinfo_endpoint ) do
154
- { :system , varname , default } ->
155
- System . get_env ( varname ) || default
156
-
157
- { :system , varname } ->
158
- System . get_env ( varname ) || Keyword . get ( default_options ( ) , :userinfo_endpoint )
159
-
160
- other ->
161
- other
162
- end
163
- end
164
-
165
143
defp with_param ( opts , key , conn ) do
166
144
if value = conn . params [ to_string ( key ) ] , do: Keyword . put ( opts , key , value ) , else: opts
167
145
end
@@ -170,18 +148,45 @@ defmodule Ueberauth.Strategy.Google do
170
148
if option ( conn , key ) , do: Keyword . put ( opts , key , option ( conn , key ) ) , else: opts
171
149
end
172
150
173
- defp oauth_client_options_from_conn ( conn ) do
174
- base_options = [ redirect_uri: callback_url ( conn ) ]
175
- request_options = conn . private [ :ueberauth_request_options ] . options
151
+ defp options_from_conn ( conn ) do
152
+ base_options = [
153
+ issuer: UeberauthGoogle.ProviderConfiguration ,
154
+ userinfo: true ,
155
+ session_key: @ session_key
156
+ ]
176
157
177
- case { request_options [ :client_id ] , request_options [ :client_secret ] } do
178
- { nil , _ } -> base_options
179
- { _ , nil } -> base_options
180
- { id , secret } -> [ client_id: id , client_secret: secret ] ++ base_options
181
- end
158
+ request_options = conn . private [ :ueberauth_request_options ] . options
159
+ oauth_options = Application . get_env ( :ueberauth , Ueberauth.Strategy.Google.OAuth ) || [ ]
160
+
161
+ [
162
+ base_options ,
163
+ request_options ,
164
+ oauth_options
165
+ ]
166
+ |> UeberauthOidcc.Config . merge_and_expand_configuration ( )
167
+ |> generate_client_secret ( )
168
+ |> fix_token_url ( )
182
169
end
183
170
184
171
defp option ( conn , key ) do
185
172
Keyword . get ( options ( conn ) , key , Keyword . get ( default_options ( ) , key ) )
186
173
end
174
+
175
+ defp generate_client_secret ( % { client_secret: { mod , fun } } = opts ) do
176
+ Map . put ( opts , :client_secret , apply ( mod , fun , [ Keyword . new ( opts ) ] ) )
177
+ end
178
+
179
+ defp generate_client_secret ( opts ) do
180
+ opts
181
+ end
182
+
183
+ defp fix_token_url ( % { token_url: token_endpoint } = opts ) do
184
+ opts
185
+ |> Map . put ( :token_endpoint , token_endpoint )
186
+ |> Map . delete ( :token_url )
187
+ end
188
+
189
+ defp fix_token_url ( opts ) do
190
+ opts
191
+ end
187
192
end
0 commit comments