From 16150f391c04fed3ccae33d4d6da17c791422042 Mon Sep 17 00:00:00 2001 From: beanieboi Date: Sat, 16 Feb 2013 22:49:44 +0100 Subject: [PATCH 1/2] add username/password authentication. first auth: start the app with --auth option and provide your username and password once this is done, the access token gets refreshed everytime you start the app --- bin/soundcloud2000 | 9 ++++++++- lib/soundcloud2000.rb | 30 ++++++++++++++++++++++++++++- lib/soundcloud2000/client.rb | 18 ++++++++++++++--- lib/soundcloud2000/models/player.rb | 2 +- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/bin/soundcloud2000 b/bin/soundcloud2000 index 4e40656..c68638e 100755 --- a/bin/soundcloud2000 +++ b/bin/soundcloud2000 @@ -2,4 +2,11 @@ require_relative '../lib/soundcloud2000' -Soundcloud2000.start +mode = ARGV.shift + +if mode == "--auth" + Soundcloud2000.auth(ARGV) +else + Soundcloud2000.refresh_token + Soundcloud2000.start +end diff --git a/lib/soundcloud2000.rb b/lib/soundcloud2000.rb index ba4e038..7a09211 100644 --- a/lib/soundcloud2000.rb +++ b/lib/soundcloud2000.rb @@ -2,7 +2,10 @@ require_relative 'soundcloud2000/application' module Soundcloud2000 - CLIENT_ID = '29f8e018e1272c27bff7d510a10da2a8' + CLIENT_ID = '29f8e018e1272c27bff7d510a10da2a8' + CLIENT_SECRET = 'MY_SECRET' + APP_FOLDER = File.expand_path("~/.soundcloud2000") + AUTH_FILE = File.join(APP_FOLDER, 'auth.json') def self.start client = Client.new(CLIENT_ID) @@ -15,4 +18,29 @@ def self.start application.run end + def self.refresh_token + client = Client.new(CLIENT_ID) + + if File.exist?(AUTH_FILE) + auth = JSON.parse(File.read(AUTH_FILE)) + + opts = { client_secret: CLIENT_SECRET, grant_type: 'refresh_token', + refresh_token: auth["refresh_token"] } + + res = client.post('/oauth2/token', opts) + File.open(AUTH_FILE, 'w') { |file| file.write(JSON.dump(res)) } + end + end + + def self.auth(credentials) + username, password = credentials + + client = Client.new(CLIENT_ID) + opts = { client_secret: CLIENT_SECRET, grant_type: 'password', + username: username, password: password } + + res = client.post('/oauth2/token', opts) + File.open(AUTH_FILE, 'w') { |file| file.write(JSON.dump(res)) } + end + end diff --git a/lib/soundcloud2000/client.rb b/lib/soundcloud2000/client.rb index 6486223..f90902f 100644 --- a/lib/soundcloud2000/client.rb +++ b/lib/soundcloud2000/client.rb @@ -30,18 +30,30 @@ def request(type, path, params={}) params[:client_id] = client_id params[:format] = 'json' + req = if type == :post + _p = Net::HTTP::Post.new("#{path}?#{uri_escape params}") + _p.set_form_data(params) + _p + else + Net::HTTP::Get.new("#{path}?#{uri_escape params}") + end + Net::HTTP.start('api.soundcloud.com', 443, :use_ssl => true) do |http| - http.request(type.new("#{path}?#{uri_escape params}")) + http.request(req) end end def get(path, params={}) - JSON.parse(request(Net::HTTP::Get, path, params).body) + JSON.parse(request(:get, path, params).body) + end + + def post(path, params={}) + JSON.parse(request(:post, path, params).body) end def location(url) uri = URI.parse(url) - res = request(Net::HTTP::Get, uri.path) + res = request(:get, uri.path) if res.code == '302' res.header['Location'] end diff --git a/lib/soundcloud2000/models/player.rb b/lib/soundcloud2000/models/player.rb index 6f5ccd8..8786dbd 100644 --- a/lib/soundcloud2000/models/player.rb +++ b/lib/soundcloud2000/models/player.rb @@ -10,7 +10,7 @@ def initialize(logger) @logger = logger @track = nil @events = Events.new - @folder = File.expand_path("~/.soundcloud2000") + @folder = APP_FOLDER @seek_speed = {} @seek_time = {} From ccbec1c6676905fd20c9a6e5f267ec59b5cc58bb Mon Sep 17 00:00:00 2001 From: beanieboi Date: Tue, 19 Feb 2013 00:15:56 +0100 Subject: [PATCH 2/2] update authentication to use the client-side flow --- bin/soundcloud2000 | 3 +- lib/soundcloud2000.rb | 29 +++--------------- lib/soundcloud2000/auth.rb | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 lib/soundcloud2000/auth.rb diff --git a/bin/soundcloud2000 b/bin/soundcloud2000 index c68638e..3c004fd 100755 --- a/bin/soundcloud2000 +++ b/bin/soundcloud2000 @@ -5,8 +5,7 @@ require_relative '../lib/soundcloud2000' mode = ARGV.shift if mode == "--auth" - Soundcloud2000.auth(ARGV) + Soundcloud2000.authenticate(ARGV) else - Soundcloud2000.refresh_token Soundcloud2000.start end diff --git a/lib/soundcloud2000.rb b/lib/soundcloud2000.rb index 7a09211..831c9d4 100644 --- a/lib/soundcloud2000.rb +++ b/lib/soundcloud2000.rb @@ -1,9 +1,9 @@ require_relative 'soundcloud2000/client' require_relative 'soundcloud2000/application' +require_relative 'soundcloud2000/auth' module Soundcloud2000 CLIENT_ID = '29f8e018e1272c27bff7d510a10da2a8' - CLIENT_SECRET = 'MY_SECRET' APP_FOLDER = File.expand_path("~/.soundcloud2000") AUTH_FILE = File.join(APP_FOLDER, 'auth.json') @@ -18,29 +18,8 @@ def self.start application.run end - def self.refresh_token - client = Client.new(CLIENT_ID) - - if File.exist?(AUTH_FILE) - auth = JSON.parse(File.read(AUTH_FILE)) - - opts = { client_secret: CLIENT_SECRET, grant_type: 'refresh_token', - refresh_token: auth["refresh_token"] } - - res = client.post('/oauth2/token', opts) - File.open(AUTH_FILE, 'w') { |file| file.write(JSON.dump(res)) } - end + def self.authenticate(credentials) + auth = Soundcloud2000::Auth.new(credentials, CLIENT_ID) + auth.authenticate_and_save end - - def self.auth(credentials) - username, password = credentials - - client = Client.new(CLIENT_ID) - opts = { client_secret: CLIENT_SECRET, grant_type: 'password', - username: username, password: password } - - res = client.post('/oauth2/token', opts) - File.open(AUTH_FILE, 'w') { |file| file.write(JSON.dump(res)) } - end - end diff --git a/lib/soundcloud2000/auth.rb b/lib/soundcloud2000/auth.rb new file mode 100644 index 0000000..3a882ec --- /dev/null +++ b/lib/soundcloud2000/auth.rb @@ -0,0 +1,62 @@ +module Soundcloud2000 + class Auth + def initialize(credentials, client_id) + @username, @password = credentials + @client_id = client_id + end + + def options + { + client_id: @client_id, + redirect_uri: 'http://developers.soundcloud.com/callback.html', + state: "SoundCloud_Dialog_#{token}", + response_type: 'code_and_token', + scope: 'non-expiring', + display: 'popup' + } + end + + def authenticate + uri_params = URI.escape(options.collect{|k,v| "#{k}=#{v}"}.join('&')) + + # need to fake the login window + login_window_request = Net::HTTP::Get.new("/connect?#{uri_params}") + login_window_result = request(login_window_request) + + # login with username and password + login_request = Net::HTTP::Post.new("/connect/login") + login_request.set_form_data(options.merge(username: @username, + password: @password)) + login_result = request(login_request) + + if login_result.code == '302' + access_token = login_result.body.match(/access_token=(.*)&/)[1] + else + puts "an error occured during authentication, please try again later" + end + end + + def authenticate_and_save + save(authenticate) + end + + def save(access_token) + return if access_token.nil? + File.open(AUTH_FILE, 'w') { |f| f.write(JSON.dump({access_token: access_token})) } + end + + def token + @token ||= rand(1000000).to_s(16) + end + + def request(request) + Net::HTTP.start('soundcloud.com', 443, use_ssl: true) do |http| + http.request(request) + end + end + + def self.access_token + JSON.parse(File.read(AUTH_FILE))["access_token"] + end + end +end