motion-authentication 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +41 -6
- data/lib/motion-authentication.rb +2 -2
- data/lib/project/motion-authentication.rb +13 -0
- data/lib/project/strategies/devise_cookie_auth.rb +57 -0
- data/lib/project/strategies/devise_token_auth.rb +9 -0
- data/lib/project/strategies/devise_token_auth_gem.rb +80 -0
- metadata +34 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ab7ad1c6522f447bb8e957077b29e783d5ec6175d67f9f08809f2089ea4c6383
|
4
|
+
data.tar.gz: aefda12e3ef3a7f8799d1d5390004a164e5379a83316fb17dcceeff3ca5d3606
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef8b4a0d2abf1ecf42ab67df9244bbbb82b5e464fcc782e7fa6fd4655ffe88b4eb2babced62efbd5178ea9791c6e83417d61397cb03199258ac698566fb74bac
|
7
|
+
data.tar.gz: 9a025671043d81dfb2a857edf810caf1964824cbb784f9ed5f32d53e10abcdf246251d9db2ae351f958921ae957a84e0d62249ac47cbc38df0c991374b3d1450
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
Currently, this library only supports iOS, but could easily support other platforms. Please submit an issue (or PR :D) for the platform you would like to support.
|
6
6
|
|
7
|
+
Need authorization? Use [`motion-authorization`](https://github.com/rubymotion-community/motion-authorization)!
|
8
|
+
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
Add this line to your application's `Gemfile`, then run `bundle install`:
|
@@ -19,13 +21,23 @@ Start by subclassing `Motion::Authentication` to create your own `Auth` class. S
|
|
19
21
|
```ruby
|
20
22
|
class Auth < Motion::Authentication
|
21
23
|
strategy DeviseTokenAuth
|
22
|
-
sign_in_url "
|
24
|
+
sign_in_url "https://example.com/api/v1/users/sign_in"
|
23
25
|
end
|
24
26
|
```
|
25
27
|
|
26
28
|
Available strategies:
|
27
29
|
|
28
|
-
* `
|
30
|
+
* `DeviseCookieAuth` - This strategy supports the default way of authenticating with Devise, just as if you were submitting the sign in form using a web browser. It works by making an initial request to fetch the authenticity token, then submits the `email` and `password`, then stores the resulting cookie for authenticating future requests. If your user model has a different name (i.e. `AdminUser`), pass along the `namespace` option (i.e. `namespace: 'admin_user'`) when calling `sign_in`. Otherwise, namespace defaults to `:user`.
|
31
|
+
|
32
|
+
* `DeviseTokenAuth` - This authentication strategy is based on [José Valim's example gist](https://gist.github.com/josevalim/fb706b1e933ef01e4fb6) and is also in the format that Ember Simple Auth Devise adapter expects ([tutorial](http://romulomachado.github.io/2015/09/28/using-ember-simple-auth-with-devise.html))
|
33
|
+
|
34
|
+
This strategy takes `email` and `password`, makes a POST request to the `sign_in_url`, and expects the response to include `email` and `token` keys in the JSON response object.
|
35
|
+
|
36
|
+
* `DeviseTokenAuthGem` - This authentication strategy is compatible with the current version of the devise_auth_token gem at https://github.com/lynndylanhurley/devise_token_auth
|
37
|
+
|
38
|
+
Signing up: this strategy takes `email`, `password` and `password_confirmation`, makes a POST request to the `sign_up_url`, and expects the response to include `uid`, `access-token` and `client` keys in the response object headers.
|
39
|
+
|
40
|
+
Signing in: this strategy takes `email` and `password`, makes a POST request to the `sign_in_url`, and expects the response to include `uid`, `access-token` and `client` keys in the response object headers.
|
29
41
|
|
30
42
|
### `.sign_in`
|
31
43
|
|
@@ -36,13 +48,11 @@ Auth.sign_in(email: email, password: password) do |result|
|
|
36
48
|
if result.success?
|
37
49
|
# authentication successful!
|
38
50
|
else
|
39
|
-
app.alert "Invalid
|
51
|
+
app.alert "Invalid email or password"
|
40
52
|
end
|
41
53
|
end
|
42
54
|
```
|
43
55
|
|
44
|
-
A successful sign in will securely store the current user's auth token.
|
45
|
-
|
46
56
|
### `.signed_in?`
|
47
57
|
|
48
58
|
You can check if an auth token has previously been stored by using `signed_in?`. For example, in your App Delegate, you might want to open your sign in screen when the app is opened:
|
@@ -59,7 +69,7 @@ end
|
|
59
69
|
|
60
70
|
### `.authorization_header`
|
61
71
|
|
62
|
-
After signing in,
|
72
|
+
After signing in, assuming you are using one of the token auth strategies, you will want to configure your API client to use your auth token in your API requests in an authorization header. Call `.authorization_header` to return the header value specific to the strategy that you are using. Two common places would be upon sign in, and when your app is launched.
|
63
73
|
|
64
74
|
```ruby
|
65
75
|
# app_delegate.rb
|
@@ -81,6 +91,31 @@ def on_load(options)
|
|
81
91
|
end
|
82
92
|
```
|
83
93
|
|
94
|
+
#### Note on `DeviseTokenAuthGem` strategy
|
95
|
+
|
96
|
+
The `devise_token_auth` gem requires all authenticated API calls to include the keys `uid`, `access-token` and `client` in the HTTP headers. Calling `.authorization_header` will return a hash with the proper key/value pairs. To include these as headers in all calls we recommend setting up your API client as follows:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
ApiClient.update_authorization_header(Auth.authorization_header)
|
100
|
+
|
101
|
+
class ApiClient
|
102
|
+
class << self
|
103
|
+
def client
|
104
|
+
@client ||= AFMotion::SessionClient.build("http://localhost:3000/") do
|
105
|
+
response_serializer :json
|
106
|
+
header "Content-Type", "application/json"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def update_authorization_header(auth_headers_hash)
|
111
|
+
auth_headers_hash.each do |key, value|
|
112
|
+
client.headers[key] = value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
84
119
|
### `.sign_out`
|
85
120
|
|
86
121
|
At some point, you're going to need to sign out. This method will clear the stored auth token, but also allows you to pass a block to be called after the token has been cleared.
|
@@ -4,8 +4,8 @@ unless defined?(Motion::Project::Config)
|
|
4
4
|
raise "This file must be required within a RubyMotion project Rakefile."
|
5
5
|
end
|
6
6
|
|
7
|
-
require 'motion-cocoapods'
|
8
|
-
require 'motion-keychain'
|
7
|
+
require 'motion-cocoapods' # TODO: this won't work for Android
|
8
|
+
require 'motion-keychain' # TODO: this won't work for Android
|
9
9
|
|
10
10
|
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
11
11
|
Motion::Project::App.setup do |app|
|
@@ -13,10 +13,23 @@ class Motion
|
|
13
13
|
@sign_in_url
|
14
14
|
end
|
15
15
|
|
16
|
+
def sign_up_url(val = nil)
|
17
|
+
@sign_up_url = val unless val.nil?
|
18
|
+
@sign_up_url
|
19
|
+
end
|
20
|
+
|
16
21
|
def sign_in(params, &block)
|
17
22
|
strategy.sign_in(sign_in_url, params, &block)
|
18
23
|
end
|
19
24
|
|
25
|
+
def sign_up(params, &block)
|
26
|
+
strategy.sign_up(sign_up_url, params, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_current_user
|
30
|
+
strategy.set_current_user
|
31
|
+
end
|
32
|
+
|
20
33
|
def authorization_header
|
21
34
|
strategy.authorization_header
|
22
35
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Motion
|
2
|
+
class Authentication
|
3
|
+
class DeviseCookieAuth
|
4
|
+
class << self
|
5
|
+
def sign_in(sign_in_url, params, &block)
|
6
|
+
get_csrf_token(sign_in_url) do |param_name, token|
|
7
|
+
namespace = params[:namespace] || :user
|
8
|
+
HTTP.post(sign_in_url, form: { namespace => params, param_name => token }, follow_redirects: false) do |response|
|
9
|
+
if response.status_code == 302 # assume success due to redirect
|
10
|
+
cookie = NSHTTPCookieStorage.sharedHTTPCookieStorage.cookiesForURL(NSURL.URLWithString(sign_in_url)).first
|
11
|
+
store_session_cookie(cookie)
|
12
|
+
block.call(true)
|
13
|
+
else # didn't redirect, must be invalid credentials
|
14
|
+
block.call(false)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_csrf_token(sign_in_url, &block)
|
21
|
+
HTTP.get(sign_in_url) do |response|
|
22
|
+
doc = Motion::HTML.parse(response.body)
|
23
|
+
param_meta_tag = doc.query('head meta[name="csrf-param"]').first
|
24
|
+
token_meta_tag = doc.query('head meta[name="csrf-token"]').first
|
25
|
+
if param_meta_tag && token_meta_tag
|
26
|
+
param_name = param_meta_tag['content']
|
27
|
+
token = token_meta_tag['content']
|
28
|
+
block.call(param_name, token)
|
29
|
+
else
|
30
|
+
mp 'Couldnt parse CSRF token from HTML'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def store_session_cookie(cookie)
|
36
|
+
MotionKeychain.set :session_cookie, JSON.generate(properties: cookie.properties)
|
37
|
+
end
|
38
|
+
|
39
|
+
def signed_in?
|
40
|
+
MotionKeychain.get(:session_cookie) && restore_session
|
41
|
+
end
|
42
|
+
|
43
|
+
def restore_session
|
44
|
+
json = MotionKeychain.get(:session_cookie)
|
45
|
+
data = JSON.parse(json)
|
46
|
+
cookie = NSHTTPCookie.cookieWithProperties(data['properties'])
|
47
|
+
NSHTTPCookieStorage.sharedHTTPCookieStorage.setCookie(cookie)
|
48
|
+
end
|
49
|
+
|
50
|
+
def sign_out(&block)
|
51
|
+
MotionKeychain.remove :session_cookie
|
52
|
+
block.call
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -11,6 +11,15 @@ module Motion
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
def sign_up(sign_up_url, params, &block)
|
15
|
+
AFMotion::JSON.post(sign_up_url, user: params) do |response|
|
16
|
+
if response.success?
|
17
|
+
store_auth_tokens(response.object)
|
18
|
+
end
|
19
|
+
block.call(response)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
14
23
|
def store_auth_tokens(data)
|
15
24
|
MotionKeychain.set :auth_email, data["email"]
|
16
25
|
MotionKeychain.set :auth_token, data["token"]
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Motion
|
2
|
+
class Authentication
|
3
|
+
class DeviseTokenAuthGem
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def sign_in(sign_in_url, params, &block)
|
7
|
+
AFMotion::JSON.post(sign_in_url, params) do |response|
|
8
|
+
if response.success?
|
9
|
+
store_auth_tokens(response.object,response.operation.response.allHeaderFields)
|
10
|
+
end
|
11
|
+
block.call(response)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign_up(sign_up_url, params, &block)
|
16
|
+
AFMotion::JSON.post(sign_up_url, params) do |response|
|
17
|
+
if response.success?
|
18
|
+
store_auth_tokens(response.object,response.operation.response.allHeaderFields)
|
19
|
+
end
|
20
|
+
block.call(response)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def store_auth_tokens(response,headers)
|
25
|
+
MotionKeychain.set :auth_uid, headers["uid"]
|
26
|
+
MotionKeychain.set :auth_token, headers["access-token"]
|
27
|
+
MotionKeychain.set :auth_client, headers["client"]
|
28
|
+
serialized_response = ""
|
29
|
+
response["data"].each do |key,value|
|
30
|
+
case key
|
31
|
+
when "assets"
|
32
|
+
value.each do |eachasset|
|
33
|
+
serialized_response << eachasset["name"] + "·" + eachasset["qty"].to_s + ","
|
34
|
+
end
|
35
|
+
when "friends"
|
36
|
+
#do nothing
|
37
|
+
else
|
38
|
+
serialized_response << key + "·" + value.to_s + ","
|
39
|
+
end
|
40
|
+
end
|
41
|
+
MotionKeychain.set :current_user, serialized_response
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_current_user
|
45
|
+
deserialize(MotionKeychain.get :current_user)
|
46
|
+
end
|
47
|
+
|
48
|
+
def authorization_header
|
49
|
+
token = MotionKeychain.get :auth_token
|
50
|
+
uid = MotionKeychain.get :auth_uid
|
51
|
+
client = MotionKeychain.get :auth_client
|
52
|
+
{"access-token" => token, "uid" => uid, "client" => client }
|
53
|
+
end
|
54
|
+
|
55
|
+
def signed_in?
|
56
|
+
!! MotionKeychain.get(:auth_token)
|
57
|
+
end
|
58
|
+
|
59
|
+
def sign_out(&block)
|
60
|
+
MotionKeychain.remove :auth_uid
|
61
|
+
MotionKeychain.remove :auth_token
|
62
|
+
MotionKeychain.remove :auth_client
|
63
|
+
MotionKeychain.remove :current_user
|
64
|
+
block.call
|
65
|
+
end
|
66
|
+
|
67
|
+
def deserialize(mystring,arr_sep=',', key_sep='·')
|
68
|
+
array = mystring.split(arr_sep)
|
69
|
+
hash = {}
|
70
|
+
array.each do |e|
|
71
|
+
key_value = e.split(key_sep)
|
72
|
+
hash[key_value[0]] = key_value[1]
|
73
|
+
end
|
74
|
+
return hash
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-authentication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Havens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: motion-cocoapods
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: motion-http
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: motion-html
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rake
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -63,8 +91,10 @@ files:
|
|
63
91
|
- README.md
|
64
92
|
- lib/motion-authentication.rb
|
65
93
|
- lib/project/motion-authentication.rb
|
94
|
+
- lib/project/strategies/devise_cookie_auth.rb
|
66
95
|
- lib/project/strategies/devise_token_auth.rb
|
67
|
-
|
96
|
+
- lib/project/strategies/devise_token_auth_gem.rb
|
97
|
+
homepage: https://github.com/rubymotion-community/motion-authentication
|
68
98
|
licenses:
|
69
99
|
- MIT
|
70
100
|
metadata: {}
|
@@ -83,8 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
113
|
- !ruby/object:Gem::Version
|
84
114
|
version: '0'
|
85
115
|
requirements: []
|
86
|
-
|
87
|
-
rubygems_version: 2.5.1
|
116
|
+
rubygems_version: 3.0.6
|
88
117
|
signing_key:
|
89
118
|
specification_version: 4
|
90
119
|
summary: A simple, standardized authentication helper for common authentication strategies
|