linkedin-oauth2 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +9 -9
- data/.gitignore +24 -39
- data/.travis.yml +4 -4
- data/.yardopts +2 -0
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +6 -5
- data/LICENSE +19 -17
- data/README.md +399 -0
- data/Rakefile +15 -22
- data/lib/linked_in/access_token.rb +24 -0
- data/lib/linked_in/api.rb +96 -3
- data/lib/linked_in/api_resource.rb +165 -0
- data/lib/linked_in/communications.rb +40 -0
- data/lib/linked_in/companies.rb +146 -0
- data/lib/linked_in/configuration.rb +41 -0
- data/lib/linked_in/connection.rb +31 -0
- data/lib/linked_in/errors.rb +33 -13
- data/lib/linked_in/groups.rb +116 -0
- data/lib/linked_in/jobs.rb +68 -0
- data/lib/linked_in/mash.rb +34 -34
- data/lib/linked_in/oauth2.rb +223 -0
- data/lib/linked_in/people.rb +141 -0
- data/lib/linked_in/search.rb +58 -43
- data/lib/linked_in/share_and_social_stream.rb +128 -0
- data/lib/linked_in/version.rb +1 -9
- data/lib/linkedin-oauth2.rb +43 -25
- data/linkedin-oauth2.gemspec +35 -21
- data/spec/linked_in/api/api_spec.rb +41 -0
- data/spec/linked_in/api/communications_spec.rb +13 -0
- data/spec/linked_in/api/companies_spec.rb +59 -0
- data/spec/linked_in/api/groups_spec.rb +55 -0
- data/spec/linked_in/api/jobs_spec.rb +33 -0
- data/spec/linked_in/api/people_spec.rb +181 -0
- data/spec/linked_in/api/search_spec.rb +71 -0
- data/spec/linked_in/api/share_and_social_stream_spec.rb +60 -0
- data/spec/linked_in/configuration_spec.rb +46 -0
- data/spec/linked_in/connection_spec.rb +10 -0
- data/spec/linked_in/module_loading_spec.rb +23 -0
- data/spec/linked_in/oauth/access_token_spec.rb +27 -0
- data/spec/linked_in/oauth/auth_code_spec.rb +86 -0
- data/spec/linked_in/oauth/credentials_spec.rb +96 -0
- data/spec/linked_in/oauth/get_access_token_spec.rb +108 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/vcr_cassettes/access_token_success.yml +84 -0
- data/spec/vcr_cassettes/bad_code.yml +78 -0
- data/spec/vcr_cassettes/companies_data.yml +44 -0
- data/spec/vcr_cassettes/invalid_access_token.yml +60 -0
- data/spec/vcr_cassettes/not_found.yml +64 -0
- data/spec/vcr_cassettes/people_picture_urls.yml +54 -0
- data/spec/vcr_cassettes/people_profile_connections_fields.yml +73 -0
- data/spec/vcr_cassettes/people_profile_connections_other.yml +78 -0
- data/spec/vcr_cassettes/people_profile_connections_self.yml +78 -0
- data/spec/vcr_cassettes/people_profile_fields_complex.yml +70 -0
- data/spec/vcr_cassettes/people_profile_fields_simple.yml +57 -0
- data/spec/vcr_cassettes/people_profile_lang_spanish.yml +59 -0
- data/spec/vcr_cassettes/people_profile_multiple_fields.yml +68 -0
- data/spec/vcr_cassettes/people_profile_multiple_uids.yml +70 -0
- data/spec/vcr_cassettes/people_profile_multiple_uids_and_urls.yml +76 -0
- data/spec/vcr_cassettes/people_profile_multiple_urls.yml +70 -0
- data/spec/vcr_cassettes/people_profile_new_connections_fields.yml +69 -0
- data/spec/vcr_cassettes/people_profile_new_connections_other.yml +72 -0
- data/spec/vcr_cassettes/people_profile_new_connections_self.yml +72 -0
- data/spec/vcr_cassettes/people_profile_other_uid.yml +59 -0
- data/spec/vcr_cassettes/people_profile_other_url.yml +59 -0
- data/spec/vcr_cassettes/people_profile_own.yml +59 -0
- data/spec/vcr_cassettes/people_profile_own_secure.yml +59 -0
- data/spec/vcr_cassettes/unauthorized.yml +61 -0
- data/spec/vcr_cassettes/unavailable.yml +81 -0
- metadata +145 -88
- data/.autotest +0 -14
- data/.document +0 -5
- data/.gemtest +0 -0
- data/.rspec +0 -1
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/README.markdown +0 -121
- data/changelog.markdown +0 -94
- data/examples/authenticate.rb +0 -16
- data/examples/network.rb +0 -12
- data/examples/profile.rb +0 -18
- data/examples/sinatra.rb +0 -69
- data/examples/status.rb +0 -6
- data/lib/linked_in/api/query_methods.rb +0 -123
- data/lib/linked_in/api/update_methods.rb +0 -76
- data/lib/linked_in/client.rb +0 -31
- data/lib/linked_in/helpers.rb +0 -6
- data/lib/linked_in/helpers/authorization.rb +0 -106
- data/lib/linked_in/helpers/request.rb +0 -93
- data/spec/cases/api_spec.rb +0 -192
- data/spec/cases/linkedin_spec.rb +0 -37
- data/spec/cases/mash_spec.rb +0 -85
- data/spec/cases/oauth_spec.rb +0 -130
- data/spec/cases/search_spec.rb +0 -190
- data/spec/helper.rb +0 -30
@@ -0,0 +1,41 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# Configuration for the LinkedIn gem.
|
3
|
+
#
|
4
|
+
# LinkedIn.configure do |config|
|
5
|
+
# config.client_id = ENV["LINKEDIN_CLIENT_ID"]
|
6
|
+
# config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# The default endpoints for LinkedIn are also stored here.
|
10
|
+
#
|
11
|
+
# LinkedIn uses the term "API key" to refer to "client id". They also
|
12
|
+
# use the term "Secret Key" to refer to "client_secret". We alias those
|
13
|
+
# terms in the config.
|
14
|
+
#
|
15
|
+
# * LinkedIn.config.site = "https://www.linkedin.com"
|
16
|
+
# * LinkedIn.config.token_url = "/uas/oauth2/accessToken"
|
17
|
+
# * LinkedIn.config.authorize_url = "/uas/oauth2/authorization"
|
18
|
+
class Configuration
|
19
|
+
attr_accessor :api,
|
20
|
+
:site,
|
21
|
+
:scope,
|
22
|
+
:client_id,
|
23
|
+
:token_url,
|
24
|
+
:api_version,
|
25
|
+
:redirect_uri,
|
26
|
+
:authorize_url,
|
27
|
+
:client_secret,
|
28
|
+
:default_profile_fields
|
29
|
+
|
30
|
+
alias_method :api_key, :client_id
|
31
|
+
alias_method :secret_key, :client_secret
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@api = "https://api.linkedin.com"
|
35
|
+
@api_version = "/v1"
|
36
|
+
@site = "https://www.linkedin.com"
|
37
|
+
@token_url = "/uas/oauth2/accessToken"
|
38
|
+
@authorize_url = "/uas/oauth2/authorization"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# Used to perform requests against LinkedIn's API.
|
3
|
+
class Connection < ::Faraday::Connection
|
4
|
+
|
5
|
+
def initialize(url=nil, options=nil, &block)
|
6
|
+
|
7
|
+
if url.is_a? Hash
|
8
|
+
options = url
|
9
|
+
url = options[:url]
|
10
|
+
end
|
11
|
+
|
12
|
+
url = default_url if url.nil?
|
13
|
+
|
14
|
+
super url, options, &block
|
15
|
+
|
16
|
+
# We need to use the FlatParamsEncoder so we can pass multiple of
|
17
|
+
# the same param to certain endpoints (like the search API).
|
18
|
+
self.options.params_encoder = ::Faraday::FlatParamsEncoder
|
19
|
+
|
20
|
+
self.response :raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private ##############################################################
|
25
|
+
|
26
|
+
|
27
|
+
def default_url
|
28
|
+
LinkedIn.config.api + LinkedIn.config.api_version
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/linked_in/errors.rb
CHANGED
@@ -1,19 +1,39 @@
|
|
1
1
|
module LinkedIn
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
|
3
|
+
# Raised when users call a deprecated function
|
4
|
+
class Deprecated < StandardError; end
|
5
|
+
|
6
|
+
# Raised when we know requests will be malformed
|
7
|
+
class InvalidRequest < StandardError; end
|
8
|
+
|
9
|
+
# Raised when LinkedIn returns a non 400+ status code during an OAuth
|
10
|
+
# request.
|
11
|
+
class OAuthError < OAuth2::Error; end
|
12
|
+
|
13
|
+
# Raised when LinkedIn returns a non 400+ status code during an API
|
14
|
+
# request
|
15
|
+
class APIError < OAuth2::Error; end
|
16
|
+
|
17
|
+
module ErrorMessages
|
18
|
+
class << self
|
19
|
+
attr_reader :deprecated,
|
20
|
+
:redirect_uri,
|
21
|
+
:no_auth_code,
|
22
|
+
:no_access_token,
|
23
|
+
:credentials_missing,
|
24
|
+
:redirect_uri_mismatch
|
9
25
|
end
|
10
26
|
|
11
|
-
|
12
|
-
|
13
|
-
|
27
|
+
@deprecated = "This has been deprecated by LinkedIn. Check https://developer.linkedin.com to see the latest available API calls"
|
28
|
+
|
29
|
+
@redirect_uri = "You must provide a redirect_uri. Set it in LinkedIn.configure or pass it in as the redirect_uri option. It must exactly match the redirect_uri you set on your application's settings page on LinkedIn's website."
|
30
|
+
|
31
|
+
@no_auth_code = "You must provide the authorization code passed to your redirect uri in the url params"
|
32
|
+
|
33
|
+
@no_access_token = "You must have an access token to use LinkedIn's API. Use the LinkedIn::OAuth2 module to obtain an access token"
|
34
|
+
|
35
|
+
@credentials_missing = "Client credentials do not exist. Please either pass your client_id and client_secret to the LinkedIn::Oauth.new constructor or set them via LinkedIn.configure"
|
14
36
|
|
15
|
-
|
16
|
-
class InformLinkedInError < StandardError; end
|
17
|
-
class NotFoundError < StandardError; end
|
37
|
+
@redirect_uri_mismatch = "Your redirect_uri must be exactly the same as the one used to generated your auth code url"
|
18
38
|
end
|
19
39
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# Groups API
|
3
|
+
#
|
4
|
+
# @see http://developer.linkedin.com/documents/groups-api Groups API
|
5
|
+
# @see http://developer.linkedin.com/documents/groups-fields Groups Fields
|
6
|
+
#
|
7
|
+
# The following API actions do not have corresponding methods in
|
8
|
+
# this module
|
9
|
+
#
|
10
|
+
# * PUT Change my Group Settings
|
11
|
+
# * POST Change my Group Settings
|
12
|
+
# * DELETE Leave a group
|
13
|
+
# * PUT Follow/unfollow a Group post
|
14
|
+
# * PUT Flag a Post as a Promotion or Job
|
15
|
+
# * DELETE Delete a Post
|
16
|
+
# * DELETE Flag a post as inappropriate
|
17
|
+
# * DELETE A comment or flag comment as inappropriate
|
18
|
+
# * DELETE Remove a Group Suggestion
|
19
|
+
#
|
20
|
+
# [(contribute here)](https://github.com/emorikawa/linkedin-oauth2)
|
21
|
+
class Groups < APIResource
|
22
|
+
# Retrieve group suggestions for the current user
|
23
|
+
#
|
24
|
+
# Permissions: r_fullprofile
|
25
|
+
#
|
26
|
+
# @see http://developer.linkedin.com/documents/job-bookmarks-and-suggestions
|
27
|
+
#
|
28
|
+
# @macro profile_options
|
29
|
+
# @return [LinkedIn::Mash]
|
30
|
+
def group_suggestions(options = {})
|
31
|
+
path = "#{profile_path(options)}/suggestions/groups"
|
32
|
+
get(path, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Retrieve the groups a current user belongs to
|
36
|
+
#
|
37
|
+
# Permissions: rw_groups
|
38
|
+
#
|
39
|
+
# @see http://developer.linkedin.com/documents/groups-api
|
40
|
+
#
|
41
|
+
# @macro profile_options
|
42
|
+
# @return [LinkedIn::Mash]
|
43
|
+
def group_memberships(options = {})
|
44
|
+
path = "#{profile_path(options)}/group-memberships"
|
45
|
+
get(path, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Retrieve the profile of a group
|
49
|
+
#
|
50
|
+
# Permissions: rw_groups
|
51
|
+
#
|
52
|
+
# @see http://developer.linkedin.com/documents/groups-api
|
53
|
+
#
|
54
|
+
# @param [Hash] options identifies the group or groups
|
55
|
+
# @optio options [String] :id identifier for the group
|
56
|
+
# @return [LinkedIn::Mash]
|
57
|
+
def group_profile(options)
|
58
|
+
path = group_path(options)
|
59
|
+
get(path, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieve the posts in a group
|
63
|
+
#
|
64
|
+
# Permissions: rw_groups
|
65
|
+
#
|
66
|
+
# @see http://developer.linkedin.com/documents/groups-api
|
67
|
+
#
|
68
|
+
# @param [Hash] options identifies the group or groups
|
69
|
+
# @optio options [String] :id identifier for the group
|
70
|
+
# @optio options [String] :count
|
71
|
+
# @optio options [String] :start
|
72
|
+
# @return [LinkedIn::Mash]
|
73
|
+
def group_posts(options)
|
74
|
+
path = "#{group_path(options)}/posts"
|
75
|
+
get(path, options)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create a share for a company that the authenticated user
|
79
|
+
# administers
|
80
|
+
#
|
81
|
+
# Permissions: rw_groups
|
82
|
+
#
|
83
|
+
# @see http://developer.linkedin.com/documents/groups-api#create
|
84
|
+
#
|
85
|
+
# @param [String] group_id Group ID
|
86
|
+
# @macro share_input_fields
|
87
|
+
# @return [void]
|
88
|
+
def add_group_share(group_id, share)
|
89
|
+
path = "/groups/#{group_id}/posts"
|
90
|
+
post(path, share)
|
91
|
+
end
|
92
|
+
|
93
|
+
# (Update) User joins, or requests to join, a group
|
94
|
+
#
|
95
|
+
# @see http://developer.linkedin.com/documents/groups-api#membergroups
|
96
|
+
#
|
97
|
+
# @param [String] group_id Group ID
|
98
|
+
# @return [void]
|
99
|
+
def join_group(group_id)
|
100
|
+
path = "/people/~/group-memberships/#{group_id}"
|
101
|
+
body = {'membership-state' => {'code' => 'member' }}
|
102
|
+
put(path, body)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
private ##############################################################
|
107
|
+
|
108
|
+
|
109
|
+
def group_path(options)
|
110
|
+
path = "/groups"
|
111
|
+
if id = options.delete(:id)
|
112
|
+
path += "/#{id}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# Jobs API
|
3
|
+
#
|
4
|
+
# @see http://developer.linkedin.com/documents/job-lookup-api-and-fields Job Lookup API and Fields
|
5
|
+
# @see http://developer.linkedin.com/documents/job-bookmarks-and-suggestions Job Bookmarks and Suggestions
|
6
|
+
#
|
7
|
+
# The following API actions do not have corresponding methods in
|
8
|
+
# this module
|
9
|
+
#
|
10
|
+
# * DELETE a Job Bookmark
|
11
|
+
class Jobs < APIResource
|
12
|
+
# Retrieve likes on a particular company update:
|
13
|
+
#
|
14
|
+
# @see http://developer.linkedin.com/reading-company-shares
|
15
|
+
#
|
16
|
+
# @param [Hash] options identifies the job
|
17
|
+
# @option options [String] id unique identifier for a job
|
18
|
+
# @return [LinkedIn::Mash]
|
19
|
+
def job(options = {})
|
20
|
+
path = jobs_path(options)
|
21
|
+
get(path, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Retrieve the current members' job bookmarks
|
25
|
+
#
|
26
|
+
# @see http://developer.linkedin.com/documents/job-bookmarks-and-suggestions
|
27
|
+
#
|
28
|
+
# @macro profile_options
|
29
|
+
# @return [LinkedIn::Mash]
|
30
|
+
def job_bookmarks(options = {})
|
31
|
+
path = "#{profile_path(options)}/job-bookmarks"
|
32
|
+
get(path, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Retrieve job suggestions for the current user
|
36
|
+
#
|
37
|
+
# @see http://developer.linkedin.com/documents/job-bookmarks-and-suggestions
|
38
|
+
#
|
39
|
+
# @macro profile_options
|
40
|
+
# @return [LinkedIn::Mash]
|
41
|
+
def job_suggestions(options = {})
|
42
|
+
path = "#{profile_path(options)}/suggestions/job-suggestions"
|
43
|
+
get(path, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a job bookmark for the authenticated user
|
47
|
+
#
|
48
|
+
# @see http://developer.linkedin.com/documents/job-bookmarks-and-suggestions
|
49
|
+
#
|
50
|
+
# @param [String] job_id Job ID
|
51
|
+
# @return [void]
|
52
|
+
def add_job_bookmark(job_id)
|
53
|
+
path = "/people/~/job-bookmarks"
|
54
|
+
post(path, {job: {id: job_id}})
|
55
|
+
end
|
56
|
+
|
57
|
+
private ##############################################################
|
58
|
+
|
59
|
+
def jobs_path(options)
|
60
|
+
path = "/jobs"
|
61
|
+
if id = options.delete(:id)
|
62
|
+
path += "/id=#{id}"
|
63
|
+
else
|
64
|
+
path += "/~"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/linked_in/mash.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
require 'hashie'
|
2
|
-
require 'multi_json'
|
3
|
-
|
4
1
|
module LinkedIn
|
2
|
+
# Coerces LinkedIn JSON to a nice Ruby hash
|
3
|
+
# LinkedIn::Mash inherits from Hashie::Mash
|
5
4
|
class Mash < ::Hashie::Mash
|
6
5
|
|
7
6
|
# a simple helper to convert a json string to a Mash
|
8
7
|
def self.from_json(json_string)
|
9
|
-
result_hash =
|
8
|
+
result_hash = JSON.load(json_string)
|
10
9
|
new(result_hash)
|
11
10
|
end
|
12
11
|
|
@@ -29,40 +28,41 @@ module LinkedIn
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
protected
|
33
31
|
|
34
|
-
|
35
|
-
self.year? && self.month? && self.day?
|
36
|
-
end
|
32
|
+
protected ############################################################
|
37
33
|
|
38
|
-
# overload the convert_key mash method so that the LinkedIn
|
39
|
-
# keys are made a little more ruby-ish
|
40
|
-
def convert_key(key)
|
41
|
-
case key.to_s
|
42
|
-
when '_key'
|
43
|
-
'id'
|
44
|
-
when '_total'
|
45
|
-
'total'
|
46
|
-
when 'values'
|
47
|
-
'all'
|
48
|
-
when 'numResults'
|
49
|
-
'total_results'
|
50
|
-
else
|
51
|
-
underscore(key)
|
52
|
-
end
|
53
|
-
end
|
54
34
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
35
|
+
def contains_date_fields?
|
36
|
+
self.year? && self.month? && self.day?
|
37
|
+
end
|
38
|
+
|
39
|
+
# overload the convert_key mash method so that the LinkedIn
|
40
|
+
# keys are made a little more ruby-ish
|
41
|
+
def convert_key(key)
|
42
|
+
case key.to_s
|
43
|
+
when '_key'
|
44
|
+
'id'
|
45
|
+
when '_total'
|
46
|
+
'total'
|
47
|
+
when 'values'
|
48
|
+
'all'
|
49
|
+
when 'numResults'
|
50
|
+
'total_results'
|
51
|
+
else
|
52
|
+
underscore(key)
|
65
53
|
end
|
54
|
+
end
|
66
55
|
|
56
|
+
# borrowed from ActiveSupport
|
57
|
+
# no need require an entire lib when we only need one method
|
58
|
+
def underscore(camel_cased_word)
|
59
|
+
word = camel_cased_word.to_s.dup
|
60
|
+
word.gsub!(/::/, '/')
|
61
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
62
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
63
|
+
word.tr!("-", "_")
|
64
|
+
word.downcase!
|
65
|
+
word
|
66
|
+
end
|
67
67
|
end
|
68
68
|
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# The LinkedIn::OAuth2::Client class. Inherits directly from [intreda/oauth2](https://github.com/intridea/oauth2)'s `OAuth2::Client`
|
3
|
+
#
|
4
|
+
# LinkedIn::OAuth2 sets the following default options:
|
5
|
+
#
|
6
|
+
# * site = "https://www.linkedin.com"
|
7
|
+
# * token_url = "/uas/oauth2/accessToken"
|
8
|
+
# * authorize_url = "/uas/oauth2/authorization"
|
9
|
+
#
|
10
|
+
# More details on LinkedIn's Authorization process can be found here: https://developer.linkedin.com/documents/authentication
|
11
|
+
class OAuth2 < ::OAuth2::Client
|
12
|
+
|
13
|
+
attr_accessor :access_token
|
14
|
+
|
15
|
+
# Instantiate a new OAuth 2.0 client using your client ID (aka API
|
16
|
+
# Key) and client secret (aka Secret Key).
|
17
|
+
#
|
18
|
+
# You should set the client_id and client_secret in the config.
|
19
|
+
#
|
20
|
+
# LinkedIn.configure do |config|
|
21
|
+
# config.client_id = ENV["LINKEDIN_CLIENT_ID"]
|
22
|
+
# config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# This will let you initialize with zero arguments.
|
26
|
+
#
|
27
|
+
# If you have already set the `client_id` and `client_secret` in your
|
28
|
+
# config, the first and only argument can be the `options` hash.
|
29
|
+
#
|
30
|
+
# @param [String] client_id the client_id value
|
31
|
+
# @param [String] client_secret the client_secret value
|
32
|
+
# @param [Hash] options the options to create the client with
|
33
|
+
# @option options [Symbol] :token_method (:post) HTTP method to use to
|
34
|
+
# request token (:get or :post)
|
35
|
+
# @option options [Hash] :connection_opts ({}) Hash of connection options
|
36
|
+
# to pass to initialize Faraday with
|
37
|
+
# @option options [FixNum] :max_redirects (5) maximum number of redirects
|
38
|
+
# to follow
|
39
|
+
# @option options [Boolean] :raise_errors (true) whether or not to
|
40
|
+
# raise an error on malformed responses
|
41
|
+
# @yield [builder] The Faraday connection builder
|
42
|
+
def initialize(client_id=LinkedIn.config.client_id,
|
43
|
+
client_secret=LinkedIn.config.client_secret,
|
44
|
+
options = {}, &block)
|
45
|
+
|
46
|
+
if client_id.is_a? Hash
|
47
|
+
options = client_id
|
48
|
+
client_id = LinkedIn.config.client_id
|
49
|
+
end
|
50
|
+
|
51
|
+
options = default_oauth_options(options)
|
52
|
+
|
53
|
+
super client_id, client_secret, options, &block
|
54
|
+
|
55
|
+
@redirect_uri = options[:redirect_uri]
|
56
|
+
|
57
|
+
if self.options[:raise_errors]
|
58
|
+
check_credentials!(client_id, client_secret)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generates the URL users use to sign into your application.
|
63
|
+
#
|
64
|
+
# Once a user enters their LinkedIn credentials, they will be
|
65
|
+
# redirected to your `redirect_uri` with the `code` parameter attached
|
66
|
+
# to it. The value of the `code` parameter can be used to get an
|
67
|
+
# access token.
|
68
|
+
#
|
69
|
+
# We recommend you set your `client_id, `client_secret`, and
|
70
|
+
# `redirect_uri` in the `LinkedIn.configure` block. They can also be
|
71
|
+
# passed in as options.
|
72
|
+
#
|
73
|
+
# @param [Hash] options the options to generate the url with
|
74
|
+
# @option options [String] :redirect_uri The url that gets redirected
|
75
|
+
# to after a successful authentication. This must exactly match the
|
76
|
+
# redirect urls setup on your LinkedIn Application Settings page.
|
77
|
+
# This option is not required if you already set the redirect_uri in
|
78
|
+
# the config.
|
79
|
+
# @option options [String] :scope A string of requested permissions
|
80
|
+
# you want from users when they authenticate with your app. If these
|
81
|
+
# are set on yoru LinkedIn Application settings page, you do not
|
82
|
+
# need to pass them in. The string must be a space-sparated,
|
83
|
+
# case-sensitive list of available scopes. See available scopes on
|
84
|
+
# LinkedIn's API documentation page.
|
85
|
+
# @option options [String] :state A long string used for CSRF
|
86
|
+
# protection. It is added as the `state` GET param in the
|
87
|
+
# redirect_uri
|
88
|
+
# @option options [Boolean] :raise_errors (true) whether or not to
|
89
|
+
# raise an error on malformed responses
|
90
|
+
def auth_code_url(options={})
|
91
|
+
options = default_auth_code_url_options(options)
|
92
|
+
|
93
|
+
if self.options[:raise_errors]
|
94
|
+
check_redirect_uri!(options)
|
95
|
+
end
|
96
|
+
|
97
|
+
@redirect_uri = options[:redirect_uri]
|
98
|
+
|
99
|
+
self.auth_code.authorize_url(options)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the access token string for the newly authenticated user.
|
103
|
+
#
|
104
|
+
# It also sets the `access_token` field on this LinkedIn::OAuth2
|
105
|
+
# instance.
|
106
|
+
#
|
107
|
+
# The required `code`
|
108
|
+
#
|
109
|
+
# @param [String] code the auth code which is passed in as a GET
|
110
|
+
# parameter to your `redirect_uri` after users authenticate your app
|
111
|
+
# @param [Hash] options
|
112
|
+
# @option options [String] :redirect_uri You normally should not have
|
113
|
+
# to pass in the redirect_uri again. If `auth_code_url` was called
|
114
|
+
# on this LinkedIn::OAuth2 instance, then the `redirect_uri` will
|
115
|
+
# already be set. This is because the `redirect_uri` in the access
|
116
|
+
# token request must exactly match the `redirect_uri` in the auth
|
117
|
+
# code url.
|
118
|
+
# @option options [Boolean] :raise_errors (true) whether or not to
|
119
|
+
# raise an error on malformed responses
|
120
|
+
def get_access_token(code=nil, options={})
|
121
|
+
check_for_code!(code)
|
122
|
+
options = default_access_code_options(options)
|
123
|
+
|
124
|
+
if self.options[:raise_errors]
|
125
|
+
check_access_code_url!(options)
|
126
|
+
end
|
127
|
+
|
128
|
+
tok = self.auth_code.get_token(code, options)
|
129
|
+
self.access_token = LinkedIn::AccessToken.new(tok.token,
|
130
|
+
tok.expires_in,
|
131
|
+
tok.expires_at)
|
132
|
+
return self.access_token
|
133
|
+
rescue ::OAuth2::Error => e
|
134
|
+
raise OAuthError.new(e.response)
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
private ##############################################################
|
139
|
+
|
140
|
+
|
141
|
+
def default_access_code_options(custom_options={})
|
142
|
+
custom_options ||= {}
|
143
|
+
options = {raise_errors: true}
|
144
|
+
|
145
|
+
@redirect_uri = LinkedIn.config.redirect_uri if @redirect_uri.nil?
|
146
|
+
options[:redirect_uri] = @redirect_uri
|
147
|
+
|
148
|
+
options = options.merge custom_options
|
149
|
+
return options
|
150
|
+
end
|
151
|
+
|
152
|
+
def default_auth_code_url_options(custom_options={})
|
153
|
+
custom_options ||= {}
|
154
|
+
options = {raise_errors: true}
|
155
|
+
|
156
|
+
if not LinkedIn.config.redirect_uri.nil?
|
157
|
+
options[:redirect_uri] = LinkedIn.config.redirect_uri
|
158
|
+
end
|
159
|
+
if not LinkedIn.config.scope.nil?
|
160
|
+
options[:scope] = LinkedIn.config.scope
|
161
|
+
end
|
162
|
+
|
163
|
+
options = options.merge custom_options
|
164
|
+
|
165
|
+
if options[:state].nil?
|
166
|
+
options[:state] = generate_csrf_token
|
167
|
+
end
|
168
|
+
|
169
|
+
return options
|
170
|
+
end
|
171
|
+
|
172
|
+
def generate_csrf_token
|
173
|
+
SecureRandom.base64(32)
|
174
|
+
end
|
175
|
+
|
176
|
+
def check_access_code_url!(options={})
|
177
|
+
check_redirect_uri!(options)
|
178
|
+
if options[:redirect_uri] != @redirect_uri
|
179
|
+
raise redirect_uri_mismatch
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def check_for_code!(code)
|
184
|
+
if code.nil?
|
185
|
+
msg = ErrorMessages.no_auth_code
|
186
|
+
raise InvalidRequest.new(msg)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def check_redirect_uri!(options={})
|
191
|
+
if options[:redirect_uri].nil?
|
192
|
+
raise redirect_uri_error
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def default_oauth_options(custom_options={})
|
197
|
+
custom_options ||= {}
|
198
|
+
options = {}
|
199
|
+
options[:site] = LinkedIn.config.site
|
200
|
+
options[:token_url] = LinkedIn.config.token_url
|
201
|
+
options[:authorize_url] = LinkedIn.config.authorize_url
|
202
|
+
return options.merge custom_options
|
203
|
+
end
|
204
|
+
|
205
|
+
def check_credentials!(client_id, client_secret)
|
206
|
+
if client_id.nil? or client_secret.nil?
|
207
|
+
raise credential_error
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def redirect_uri_error
|
212
|
+
InvalidRequest.new ErrorMessages.redirect_uri
|
213
|
+
end
|
214
|
+
|
215
|
+
def credential_error
|
216
|
+
InvalidRequest.new ErrorMessages.credentials_missing
|
217
|
+
end
|
218
|
+
|
219
|
+
def redirect_uri_mismatch
|
220
|
+
InvalidRequest.new ErrorMessages.redirect_uri_mismatch
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|