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.
Files changed (95) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +24 -39
  3. data/.travis.yml +4 -4
  4. data/.yardopts +2 -0
  5. data/CHANGELOG.md +10 -0
  6. data/CONTRIBUTING.md +1 -0
  7. data/Gemfile +6 -5
  8. data/LICENSE +19 -17
  9. data/README.md +399 -0
  10. data/Rakefile +15 -22
  11. data/lib/linked_in/access_token.rb +24 -0
  12. data/lib/linked_in/api.rb +96 -3
  13. data/lib/linked_in/api_resource.rb +165 -0
  14. data/lib/linked_in/communications.rb +40 -0
  15. data/lib/linked_in/companies.rb +146 -0
  16. data/lib/linked_in/configuration.rb +41 -0
  17. data/lib/linked_in/connection.rb +31 -0
  18. data/lib/linked_in/errors.rb +33 -13
  19. data/lib/linked_in/groups.rb +116 -0
  20. data/lib/linked_in/jobs.rb +68 -0
  21. data/lib/linked_in/mash.rb +34 -34
  22. data/lib/linked_in/oauth2.rb +223 -0
  23. data/lib/linked_in/people.rb +141 -0
  24. data/lib/linked_in/search.rb +58 -43
  25. data/lib/linked_in/share_and_social_stream.rb +128 -0
  26. data/lib/linked_in/version.rb +1 -9
  27. data/lib/linkedin-oauth2.rb +43 -25
  28. data/linkedin-oauth2.gemspec +35 -21
  29. data/spec/linked_in/api/api_spec.rb +41 -0
  30. data/spec/linked_in/api/communications_spec.rb +13 -0
  31. data/spec/linked_in/api/companies_spec.rb +59 -0
  32. data/spec/linked_in/api/groups_spec.rb +55 -0
  33. data/spec/linked_in/api/jobs_spec.rb +33 -0
  34. data/spec/linked_in/api/people_spec.rb +181 -0
  35. data/spec/linked_in/api/search_spec.rb +71 -0
  36. data/spec/linked_in/api/share_and_social_stream_spec.rb +60 -0
  37. data/spec/linked_in/configuration_spec.rb +46 -0
  38. data/spec/linked_in/connection_spec.rb +10 -0
  39. data/spec/linked_in/module_loading_spec.rb +23 -0
  40. data/spec/linked_in/oauth/access_token_spec.rb +27 -0
  41. data/spec/linked_in/oauth/auth_code_spec.rb +86 -0
  42. data/spec/linked_in/oauth/credentials_spec.rb +96 -0
  43. data/spec/linked_in/oauth/get_access_token_spec.rb +108 -0
  44. data/spec/spec_helper.rb +15 -0
  45. data/spec/vcr_cassettes/access_token_success.yml +84 -0
  46. data/spec/vcr_cassettes/bad_code.yml +78 -0
  47. data/spec/vcr_cassettes/companies_data.yml +44 -0
  48. data/spec/vcr_cassettes/invalid_access_token.yml +60 -0
  49. data/spec/vcr_cassettes/not_found.yml +64 -0
  50. data/spec/vcr_cassettes/people_picture_urls.yml +54 -0
  51. data/spec/vcr_cassettes/people_profile_connections_fields.yml +73 -0
  52. data/spec/vcr_cassettes/people_profile_connections_other.yml +78 -0
  53. data/spec/vcr_cassettes/people_profile_connections_self.yml +78 -0
  54. data/spec/vcr_cassettes/people_profile_fields_complex.yml +70 -0
  55. data/spec/vcr_cassettes/people_profile_fields_simple.yml +57 -0
  56. data/spec/vcr_cassettes/people_profile_lang_spanish.yml +59 -0
  57. data/spec/vcr_cassettes/people_profile_multiple_fields.yml +68 -0
  58. data/spec/vcr_cassettes/people_profile_multiple_uids.yml +70 -0
  59. data/spec/vcr_cassettes/people_profile_multiple_uids_and_urls.yml +76 -0
  60. data/spec/vcr_cassettes/people_profile_multiple_urls.yml +70 -0
  61. data/spec/vcr_cassettes/people_profile_new_connections_fields.yml +69 -0
  62. data/spec/vcr_cassettes/people_profile_new_connections_other.yml +72 -0
  63. data/spec/vcr_cassettes/people_profile_new_connections_self.yml +72 -0
  64. data/spec/vcr_cassettes/people_profile_other_uid.yml +59 -0
  65. data/spec/vcr_cassettes/people_profile_other_url.yml +59 -0
  66. data/spec/vcr_cassettes/people_profile_own.yml +59 -0
  67. data/spec/vcr_cassettes/people_profile_own_secure.yml +59 -0
  68. data/spec/vcr_cassettes/unauthorized.yml +61 -0
  69. data/spec/vcr_cassettes/unavailable.yml +81 -0
  70. metadata +145 -88
  71. data/.autotest +0 -14
  72. data/.document +0 -5
  73. data/.gemtest +0 -0
  74. data/.rspec +0 -1
  75. data/.ruby-gemset +0 -1
  76. data/.ruby-version +0 -1
  77. data/README.markdown +0 -121
  78. data/changelog.markdown +0 -94
  79. data/examples/authenticate.rb +0 -16
  80. data/examples/network.rb +0 -12
  81. data/examples/profile.rb +0 -18
  82. data/examples/sinatra.rb +0 -69
  83. data/examples/status.rb +0 -6
  84. data/lib/linked_in/api/query_methods.rb +0 -123
  85. data/lib/linked_in/api/update_methods.rb +0 -76
  86. data/lib/linked_in/client.rb +0 -31
  87. data/lib/linked_in/helpers.rb +0 -6
  88. data/lib/linked_in/helpers/authorization.rb +0 -106
  89. data/lib/linked_in/helpers/request.rb +0 -93
  90. data/spec/cases/api_spec.rb +0 -192
  91. data/spec/cases/linkedin_spec.rb +0 -37
  92. data/spec/cases/mash_spec.rb +0 -85
  93. data/spec/cases/oauth_spec.rb +0 -130
  94. data/spec/cases/search_spec.rb +0 -190
  95. 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
@@ -1,19 +1,39 @@
1
1
  module LinkedIn
2
- module Errors
3
- class LinkedInError < StandardError
4
- attr_reader :data
5
- def initialize(data)
6
- @data = data
7
- super
8
- end
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
- class UnauthorizedError < LinkedInError; end
12
- class GeneralError < LinkedInError; end
13
- class AccessDeniedError < LinkedInError; end
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
- class UnavailableError < StandardError; end
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
@@ -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 = ::MultiJson.decode(json_string)
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
- def contains_date_fields?
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
- # borrowed from ActiveSupport
56
- # no need require an entire lib when we only need one method
57
- def underscore(camel_cased_word)
58
- word = camel_cased_word.to_s.dup
59
- word.gsub!(/::/, '/')
60
- word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
61
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
62
- word.tr!("-", "_")
63
- word.downcase!
64
- word
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