linkedin-oauth2 0.1.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|