fidor_starter_kits 0.3.3 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 455dac3a1472751eff2af9a065ace07903a0a774
4
- data.tar.gz: 8f704e362c9743db66a256a81cff4038d948e60b
3
+ metadata.gz: 0d9257e8f3fd57a085a5bfad18e7d08e17f0410d
4
+ data.tar.gz: 6cc26babb8d847a711d48556b3285b3a3543cb70
5
5
  SHA512:
6
- metadata.gz: 36551a95617bf621d341c7c12c99e04e4f82eca851f440867579aa600e97aea8a4a8ba9ce22af19e7b8c87b3cab4245493b23d5a6038de83cfc5c79815f73683
7
- data.tar.gz: c6c1ed02a764094ee1c559aae45923dcf05a713696c412f7afa10fedb9c9258b30d54ee9100d55ac52c0703b9f5f50441c2a347ff90ba828b457b075516c0a79
6
+ metadata.gz: 475514d665725df63f3c2047df7b0dd7878f0c8b838045f80603829a938d0e1f4f3dc4e32a05e04e1edee4c9c9d24b5684a323e5c035b8c7fa7a30d9e6e1e101
7
+ data.tar.gz: effb9982ec4dea6dab0b50460f6c527b72686259dad4ff31d71d63a8a48a178ccf3e802d36bc8034142174ddf44b3052c22a33085d6bbd0efb4d24d80ff5d30a
data/CHANGELOG.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Changelog Fidor Admin API Schema
2
2
  See [commit messages](https://github.com/fidor/fidor_starter_kits/commits/) for details.
3
3
 
4
- ##2015-01 v.0.3.2
5
- *bugfix: proper TLS handling for node example
4
+ ##2015-06
5
+
6
+ * fix usage of access_token in api URLs, now set in Authorization Header
7
+
8
+ ##2015-01
9
+
10
+ *fix proper TLS handling for node example
6
11
 
7
12
  ##2014-12
8
13
 
data/README.md CHANGED
@@ -11,7 +11,6 @@ information into the source files and delivers the example as zipped download.
11
11
  If you choose the manual way to explore our example apps, simply checkout this
12
12
  repo, register and app and add the required credentials, URL's to the sources.
13
13
 
14
-
15
14
  For Ruby Heros, this repo is also available as ruby gem and provides a tiny
16
15
  helper for app creation, see Install & Usage.
17
16
 
@@ -57,18 +56,18 @@ application manager. Before the following placeholders inside in your main
57
56
  example.xy file are substituted with the according values from the app
58
57
  (client_id/secret) and the values in .fidor_meta.json:
59
58
 
60
- <APP_URL> # default http://localhost:8000/example.php
59
+ <APP_URL> # default http://localhost:8000/example.php
61
60
  <CLIENT_ID>
62
61
  <CLIENT_SECRET>
63
- <FIDOR_OAUTH_URL> # e.g https://fidor.com/oauth
64
- <FIDOR_API_URL>
62
+ <FIDOR_OAUTH_URL> # e.g Sandbox: https://aps.fidor.de/oauth / Live: https://apm.fidor.de/oauth
63
+ <FIDOR_API_URL> # e.g Sandbox: https://aps.fidor.de / Live: https://api.fidor.de
65
64
 
66
65
  So just add those to example.[rb, php, ..] and see existing examples and specs
67
66
  for a reference.
68
67
 
69
68
  ## Contributing
70
69
 
71
- 1. Fork it ( http://github.com/schorsch/fidor_starter_kits/fork )
70
+ 1. Fork it (https://github.com/fidor/fidor_starter_kits/fork )
72
71
  2. Create your feature branch (`git checkout -b my-new-feature`)
73
72
  3. Commit your changes (`git commit -am 'Add some feature'`)
74
73
  4. Push to the branch (`git push origin my-new-feature`)
@@ -1,3 +1,3 @@
1
1
  module FidorStarterKits
2
- VERSION = '0.3.3'
3
- end
2
+ VERSION = '0.3.5'
3
+ end
@@ -6,7 +6,7 @@ require 'json'
6
6
 
7
7
  module FidorStarterKits
8
8
 
9
- STARTER_KITS = %w{ node_tx golang_plain php_plain sinatra_plain java_servlet }
9
+ STARTER_KITS = %w{ node_tx golang_plain php_oauth_plain ruby_oauth_plain java_servlet }
10
10
 
11
11
  class << self
12
12
 
@@ -70,7 +70,7 @@ module FidorStarterKits
70
70
  if File.exists? meta
71
71
  File.open(meta) {|f| @conf[kit] = JSON.parse(f.read)}
72
72
  else
73
- @conf[kit] = "meh"
73
+ @conf[kit] = {"error" => ".fidor_meta.json not found"}
74
74
  end
75
75
  end
76
76
  return @conf
@@ -80,8 +80,8 @@ module FidorStarterKits
80
80
  all[app_name]
81
81
  end
82
82
 
83
- def each
84
- all.each_value { |conf| yield conf }
83
+ def each
84
+ all.each_value { |conf| yield conf }
85
85
  end
86
86
 
87
87
  end
@@ -4,10 +4,10 @@ describe FidorStarterKits do
4
4
 
5
5
  describe '.exists?' do
6
6
  it 'is true' do
7
- expect( FidorStarterKits.exists?('sinatra_plain') ).to be
7
+ expect( FidorStarterKits.exists?('ruby_oauth_plain') ).to be
8
8
  end
9
9
  it 'is false' do
10
- expect( FidorStarterKits.exists?('sinatra_plain') ).to be
10
+ expect( FidorStarterKits.exists?('ruby_oauth_plain') ).to be
11
11
  end
12
12
  end
13
13
 
@@ -21,7 +21,7 @@ describe FidorStarterKits do
21
21
 
22
22
  it 'creates zip file' do
23
23
  opts = {
24
- app_name: 'sinatra_plain',
24
+ app_name: 'ruby_oauth_plain',
25
25
  client_id: '123',
26
26
  client_secret: '12345',
27
27
  app_url: 'localhost'
@@ -32,7 +32,7 @@ describe FidorStarterKits do
32
32
 
33
33
  it 'replaces placeholders in example.rb' do
34
34
  opts = {
35
- app_name: 'sinatra_plain',
35
+ app_name: 'ruby_oauth_plain',
36
36
  client_id: 'my-client-id',
37
37
  client_secret: 'my-client-secret',
38
38
  app_url: 'my-app-url',
@@ -49,7 +49,7 @@ describe FidorStarterKits do
49
49
  end
50
50
  end
51
51
 
52
- describe '.all' do
52
+ describe '.all' do
53
53
  it 'lists all starter kits' do
54
54
  expect(FidorStarterKits.all.count).to eq(FidorStarterKits::STARTER_KITS.size)
55
55
  end
@@ -58,8 +58,8 @@ describe FidorStarterKits do
58
58
  conf = FidorStarterKits.all
59
59
  expect(conf["golang_plain"]["display_name"]).to eq("Go Plain")
60
60
  expect(conf["node_tx"]["description"]).to eq("A simple nodejs based app, showing how to get user transactions")
61
- expect(conf["php_plain"]["app_name"]).to eq("php_plain")
62
- expect(conf["sinatra_plain"]["app_url"]).to eq("http://localhost:4567")
61
+ expect(conf["php_oauth_plain"]["app_name"]).to eq("php_oauth_plain")
62
+ expect(conf["ruby_oauth_plain"]["app_url"]).to eq("http://localhost:4567")
63
63
  expect(conf["java_servlet"]["callback_urls"]).to eq("http://localhost:8080/JavaServlet/Example")
64
64
  end
65
65
  end
@@ -12,78 +12,189 @@ package main
12
12
  // $ http://localhost:8080
13
13
 
14
14
  import (
15
+ "crypto/rand"
16
+ "encoding/hex"
15
17
  "encoding/json"
18
+ "errors"
16
19
  "fmt"
20
+ "io"
17
21
  "net/http"
18
22
  "net/url"
19
- "errors"
23
+ "strings"
20
24
  )
21
25
 
22
26
  // The following sections define the settings you require for the
23
27
  // app to be able to connect to and authorize itself against the api.
24
28
 
25
- // app ID and secret, can be found in this apps "Details" page in the
26
- // AppManager.
27
- var client_id = "<CLIENT_ID>"
28
- var client_secret = "<CLIENT_SECRET>"
29
-
30
- // Fidor's OAuth Endpoint (this changes between Sandbox and Production)
31
- var fidor_oauth_url = "<FIDOR_OAUTH_URL>" // e.g https://fidor.com/api_sandbox/oauth
32
- // The OAuth Endpoint this App provides
33
- var oauth_cb_url = "<APP_URL>"
34
-
35
- // The URL of the Fidor API (this changes between Sandbox and
36
- // Production)
37
- var fidor_api_url = "<FIDOR_API_URL>" // e.g https://fidor.com/api_sandbox vs /api
38
-
39
-
29
+ type Config struct {
30
+ AppUrl string // where to reach this application
31
+ ClientId string // OAuth Client_id parameter
32
+ ClientSecret string // OAuth Client_secret parameter
33
+ FidorApiUrl string // API endpoint (this changes between Sandbox and Production)
34
+ FidorOauthUrl string // OAuth endpoint (this changes between Sandbox and Production)
35
+ }
40
36
 
37
+ var fidorConfig = Config{
38
+ AppUrl: "<APP_URL>",
39
+ ClientId: "<CLIENT_ID>",
40
+ ClientSecret: "<CLIENT_SECRET>",
41
+ FidorApiUrl: "<FIDOR_API_URL>",
42
+ FidorOauthUrl: "<FIDOR_OAUTH_URL>",
43
+ }
41
44
 
42
45
  func main() {
43
46
  // register a handler function (see next function) to service
44
47
  // requests ...
45
48
  http.HandleFunc("/", indexHandler)
46
- fmt.Printf("Now open http://localhost:8080")
49
+ fmt.Printf("Now open %s\n", fidorConfig.AppUrl)
47
50
  // ... and start listening.
48
- http.ListenAndServe(":8080", nil)
51
+ if u, err := url.Parse(fidorConfig.AppUrl); err != nil {
52
+ fmt.Printf("Can't make sense of configured url: %s\nBye.\n", fidorConfig.AppUrl)
53
+ } else {
54
+ var hostPort = strings.Split(u.Host, ":")
55
+ var port = ":8080"
56
+ if len(hostPort) == 2 {
57
+ port = ":" + hostPort[1]
58
+ }
59
+ http.ListenAndServe(port, nil)
60
+ }
49
61
  }
50
62
 
51
63
  func indexHandler(w http.ResponseWriter, r *http.Request) {
64
+ if r.Method != "GET" {
65
+ w.WriteHeader(403)
66
+ return
67
+ }
52
68
 
53
- // ignore any favicon requests, etc.
54
- if r.URL.Path != "/" {
69
+ switch r.URL.Path {
70
+ case "/":
71
+ renderWelcome(w)
72
+ case "/transactions":
73
+ render("/transactions", w, r)
74
+ case "/accounts":
75
+ render("/accounts", w, r)
76
+ case "/oauth":
77
+ handleOAuthCallback(w, r)
78
+ case "/logout":
79
+ handleLogout(w, r, "/")
80
+ default:
55
81
  w.WriteHeader(404)
56
- return
57
82
  }
83
+ }
84
+
85
+ const COOKIE_NAME = "GO_SESSION"
86
+
87
+ // session database, available access_tokens are mapped to their cookie-session keys
88
+ var sessions = make(map[string]string)
89
+
90
+ // stores a retireved access_token both on the server and the reference in a client-side cookie.
91
+ func createSession(w http.ResponseWriter, accessToken string) {
92
+ println(accessToken)
93
+ rnd := make([]byte, 20, 20)
94
+ rand.Read(rnd)
95
+ session := hex.EncodeToString(rnd)
96
+ sessions[session] = accessToken
97
+ cookie := http.Cookie{
98
+ Name: COOKIE_NAME,
99
+ Value: session,
100
+ }
101
+ http.SetCookie(w, &cookie)
102
+ }
103
+
104
+ // proxies an API endpoint. In case the client is not logged in (i.e. no
105
+ // session cookie is set), the OAuth procedure is initiated. Once the
106
+ // client is logged, a call the the `endpoint` API endpoint is made and
107
+ // the results (including errors) are piped to the client.
108
+ func render(endpoint string, w http.ResponseWriter, r *http.Request) {
109
+ // check if request has cookie set
110
+ if cookie, err := r.Cookie(COOKIE_NAME); err != nil {
111
+ // else redirect to OAuth Authorization EP
112
+ redirectToOAuth(w, r, endpoint)
113
+ } else {
114
+ session := cookie.Value
115
+ accessToken := sessions[session]
58
116
 
59
- // check whether we have a GET parameter named `code`, if so,
60
- // this is a redirect back from the Fidor OAuth server.
61
- values := r.URL.Query()
62
- if values["code"] != nil {
63
- // retrieve the actual OAuth access token ....
64
- code := values.Get("code")
65
- if token, err := retrieveTokenFromCode(code); err != nil {
66
- fmt.Printf("err: %v\n", err)
117
+ // pipe api endpoint
118
+ ep := fmt.Sprintf("%s/%s", fidorConfig.FidorApiUrl, endpoint)
119
+ if api_req, err := http.NewRequest("GET", ep, nil); err != nil {
67
120
  w.WriteHeader(500)
68
- fmt.Fprintf(w, "Unfortunately, an error occurred retrieving oauth token")
121
+ w.Write([]byte(err.Error()))
69
122
  } else {
70
- // ... and finally, greet the user and assemble links
71
- renderWelcome(w, token)
123
+ api_req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
124
+
125
+ client := &http.Client{}
126
+ if api_resp, err := client.Do(api_req); err != nil {
127
+ w.WriteHeader(500)
128
+ w.Write([]byte(err.Error()))
129
+ } else {
130
+ if api_resp.StatusCode == 401 { // token probably expired
131
+ handleLogout(w, r, endpoint)
132
+ return
133
+ }
134
+ contentType := http.CanonicalHeaderKey("content-type")
135
+ w.Header().Set(contentType, api_resp.Header.Get(contentType))
136
+ w.WriteHeader(api_resp.StatusCode)
137
+ io.Copy(w, api_resp.Body)
138
+ }
72
139
  }
140
+ }
141
+ }
142
+
143
+ // client browser is redirected to `authorize` oauth endpoints. The
144
+ // `target_endpoint` parameter indicates which application endpoint to
145
+ // redirect the client to once the access_token has been retrieved.
146
+ func redirectToOAuth(w http.ResponseWriter, r *http.Request, target_endpoint string) {
147
+ _redirectURI := fmt.Sprintf("%s/oauth?ep=%s", fidorConfig.AppUrl, target_endpoint)
148
+ redirectURI := url.QueryEscape(_redirectURI)
149
+
150
+ oauthRedirectURL := fmt.Sprintf("%s/authorize?client_id=%s&state=321&response_type=code&redirect_uri=%s", fidorConfig.FidorOauthUrl, fidorConfig.ClientId, redirectURI)
151
+ http.Redirect(w, r, oauthRedirectURL, 307)
152
+ }
153
+
154
+ // handles the oauth callback (i.e. the redirect that the oauth
155
+ // authorize call "returns" to the client)
156
+ func handleOAuthCallback(w http.ResponseWriter, r *http.Request) {
157
+ code := r.FormValue("code")
158
+ target := r.FormValue("ep")
159
+
160
+ if code == "" || target == "" {
161
+ w.WriteHeader(500)
162
+ w.Write([]byte("missing code or target ep"))
163
+ return
164
+ }
165
+
166
+ if token, err := retrieveTokenFromCode(code, target); err != nil {
167
+ w.WriteHeader(500)
168
+ w.Write([]byte(err.Error()))
169
+ return
73
170
  } else {
74
- // we don't have an oauth `code` yet, so we need to
75
- // redirect the user to the OAuth provider to get one ...
76
- oauth_url := fmt.Sprintf("%s/authorize?client_id=%s&state=123&response_type=code&redirect_uri=%s",
77
- fidor_oauth_url,
78
- client_id,
79
- url.QueryEscape(oauth_cb_url))
80
-
81
- header := w.Header()
82
- header.Add("location", oauth_url)
83
- w.WriteHeader(307)
171
+ createSession(w, token)
172
+ http.Redirect(w, r, target, 307)
84
173
  }
85
174
  }
86
175
 
176
+ // clear our application's session, forces user to call the oauth
177
+ // authorize endpoint again. This may or may not force the user to have
178
+ // to reenter credentials, depending on whether the user's Fidor session
179
+ // has expired.
180
+ func handleLogout(w http.ResponseWriter, r *http.Request, endpoint string) {
181
+ if cookie, err := r.Cookie(COOKIE_NAME); err == nil {
182
+ // we have a cookie
183
+
184
+ // remove it from our stored sessions
185
+ session := cookie.Value
186
+ delete(sessions, session)
187
+
188
+ // kill the cookie on the client
189
+ cookie := http.Cookie{
190
+ Name: COOKIE_NAME,
191
+ Value: "",
192
+ MaxAge: -1,
193
+ }
194
+ http.SetCookie(w, &cookie)
195
+ }
196
+ http.Redirect(w, r, endpoint, 307)
197
+ }
87
198
 
88
199
  // Our TokenResponse representation used to pick it out from the JSON
89
200
  // returned by the OAuth server.
@@ -92,18 +203,22 @@ type TokenResponse struct {
92
203
  }
93
204
 
94
205
  // Use the OAuth code that the user's browser picked up from the OAuth
95
- // server to request an OAuth access_token to use in API requests.
96
- func retrieveTokenFromCode(code string) (token string, err error) {
206
+ // server to request an OAuth access_token to use in API requests. The
207
+ // `target_endpoint` parameter indicates which of this app's endpoints
208
+ // to redirect the client to once we have the access_token and stored a
209
+ // reference in the client's cookie.
210
+ func retrieveTokenFromCode(code string, target_endpoint string) (token string, err error) {
97
211
  // assemble the API endpoint URL and request payload
98
- tokenUrl := fmt.Sprintf("%s/token", fidor_oauth_url)
212
+ redirect_uri := fmt.Sprintf("%s/oauth?ep=%s", fidorConfig.AppUrl, target_endpoint)
99
213
  tokenPayload := url.Values{
100
- "client_id": {client_id},
101
- "client_secret": {client_secret},
214
+ "client_id": {fidorConfig.ClientId},
215
+ "client_secret": {fidorConfig.ClientSecret},
102
216
  "code": {code},
103
- "redirect_uri": {url.QueryEscape(oauth_cb_url)},
217
+ "redirect_uri": {url.QueryEscape(redirect_uri)},
104
218
  "grant_type": {"authorization_code"},
105
219
  }
106
- // Call API
220
+ // Call the Oauth `token` endpoint
221
+ tokenUrl := fmt.Sprintf("%s/token", fidorConfig.FidorOauthUrl)
107
222
  if resp, err := http.PostForm(tokenUrl, tokenPayload); err != nil {
108
223
  println(err)
109
224
  return "", err
@@ -122,44 +237,8 @@ func retrieveTokenFromCode(code string) (token string, err error) {
122
237
  }
123
238
  }
124
239
 
125
-
126
- // Our server code only makes a single call to the API to retrieve user
127
- // information. This is our internal representation of the returned JSON
128
- // used to pick out the user's email.
129
- type UserResponse struct {
130
- Email string `json:"email"`
131
- }
132
-
133
- // function to retrieve user information from the API
134
- func getUser(token string) (u UserResponse, err error) {
135
- // Assemble endpoint URL...
136
- url := fmt.Sprintf("%s/users/current?access_token=%s", fidor_api_url, token)
137
- if resp, err := http.Get(url); err != nil {
138
- return u, err
139
- } else {
140
- decoder := json.NewDecoder(resp.Body)
141
- if err = decoder.Decode(&u); err != nil {
142
- return u, err
143
- } else {
144
- return u, nil
145
- }
146
- }
147
- }
148
-
149
-
150
- // once all the OAuth calls have been taken care of, this function is
151
- // called from the http handler. It retrieves the user's email address
152
- // and inserts links to `transaction` and `accounts` endpoints.
153
-
154
- func renderWelcome(w http.ResponseWriter, token string) {
155
- if user, err := getUser(token); err != nil {
156
- fmt.Printf("err: %v\n", err)
157
- w.WriteHeader(500)
158
- } else {
159
- txLink := fmt.Sprintf("%s/transactions?access_token=%s", fidor_api_url, token)
160
- acctsLink := fmt.Sprintf("%s/accounts?access_token=%s", fidor_api_url, token)
161
- fmt.Fprintf(w, indexTemplate, user.Email, token, txLink, acctsLink)
162
- }
240
+ func renderWelcome(w http.ResponseWriter) {
241
+ w.Write([]byte(indexTemplate))
163
242
  }
164
243
 
165
244
  var indexTemplate = `
@@ -167,10 +246,10 @@ var indexTemplate = `
167
246
  <head>
168
247
  </head>
169
248
  <body>
170
- <h1>Welcome %s!</h1>
171
- <i>retrieved <tt>access_token</tt>: %s</i>
172
- <p><a href="%s">Transactions</a></p>
173
- <p><a href="%s">Accounts</a></p>
249
+ <h1>Welcome!</h1>
250
+ <p><a href="/transactions">Transactions</a></p>
251
+ <p><a href="/accounts">Accounts</a></p>
252
+ <p><a href="/logout">Log Out</a></p>
174
253
  </body>
175
254
  </html>
176
255
  `
@@ -4,35 +4,49 @@
4
4
  //
5
5
  // This sample intentionally does not require any further dependencies.
6
6
  // We tried to keep things as simple as possible in order to illustrate the
7
- // underlying mechanisms of the API.
7
+ // underlying mechanisms of the API. This comes at the expense of having
8
+ // to handled cookies and session in the sample...
8
9
  //
9
10
  // Using this code
10
11
  // ---------------
11
12
  //
13
+ // The provided source code is divided into three sections:
14
+ // 1.) Handling OAuth calls
15
+ // 2.) Cookie and Session Handling
16
+ // 3.) Handling Browser Requests
17
+ //
12
18
  // In order to use this code, a sample app needs to be installed in the
13
19
  // Fidor App Manager. Settings for this app are displayed in the app
14
- // manager an need to be transfered into the config below:
20
+ // manager an need to be transfered into the config below, if you
21
+ // downloaded the code directly from the Fidor App Manager, these
22
+ // settings should be filled in for you:
15
23
 
16
24
  var fidor_config = {
17
25
  app_url : "<APP_URL>",
18
26
  client_id : "<CLIENT_ID>",
19
27
  client_secret : "<CLIENT_SECRET>",
20
- fidor_api_url : "<FIDOR_API_URL>"
28
+ fidor_api_url : "<FIDOR_API_URL>",
29
+ fidor_oauth_url: "<FIDOR_OAUTH_URL>"
21
30
  }
22
31
 
23
32
 
33
+ /************************************************************************
34
+ /* BEGIN: OAuth Calls
35
+ ************************************************************************/
24
36
 
25
-
37
+ //
26
38
  // redirect the user to the OAuth authorization endpoint with the
27
39
  // following params:
28
40
  // - client_id
29
41
  // - state
30
42
  // - response_type
31
43
  // - redirect_uri
32
- function redirect_to_oauth(response){
33
- var redirect_uri = encodeURIComponent(fidor_config.app_url)
34
- var oauth_url = fidor_config.fidor_api_url+
35
- "/oauth/authorize?client_id="+fidor_config.client_id+
44
+ //
45
+ function redirect_to_oauth(response, target_endpoint){
46
+ var _redirect_uri = fidor_config.app_url+"/oauth?ep="+target_endpoint
47
+ var redirect_uri = encodeURIComponent(_redirect_uri)
48
+ var oauth_url = fidor_config.fidor_oauth_url+
49
+ "/authorize?client_id="+fidor_config.client_id+
36
50
  "&state=123&response_type=code&"+
37
51
  "redirect_uri="+redirect_uri
38
52
  response.writeHead(307, {"location" : oauth_url})
@@ -40,30 +54,45 @@ function redirect_to_oauth(response){
40
54
  return
41
55
  }
42
56
 
57
+ //
43
58
  // Execute a POST request against the OAUTH token endpoint
44
59
  // in order to exchange: code, client_id, client_secret,
45
- // rerdirect_uri and grant_type for an auth_token.
46
- function retrieve_access_token_from_code( code, cb ) {
47
- var oauth_url = url.parse(fidor_config.fidor_api_url)
60
+ // redirect_uri and grant_type for an auth_token.
61
+ //
62
+ // This corresponds to OAuth 3.2 Token Endpoint / 4.1.3 Access Token
63
+ // Request.
64
+ //
65
+ // Parameter:
66
+ // - the code that was returned from the Authorization Endpoint
67
+ // - the target endpoint (transactions/ or accounts/) that was requested
68
+ // in order to reconstruct the 'redirect-uri' parameter of the
69
+ // Authorization call.
70
+ // - callback(error, access_token)
71
+ //
72
+ function retrieve_access_token_from_code( code, target_endpoint, cb ) {
73
+ var oauth_url = url.parse(fidor_config.fidor_oauth_url)
74
+
48
75
  // where to send the data ...
49
76
  var postOptions = {
50
77
  method: "POST",
51
- path : oauth_url.path+"/oauth/token",
78
+ path : oauth_url.path+"/token",
52
79
  port : oauth_url.port,
53
80
  host : oauth_url.hostname
54
81
  }
55
82
 
56
83
  // ... what to send
84
+ var redirect_uri = fidor_config.app_url+"/oauth?ep="+target_endpoint
57
85
  var postData = {
58
86
  code : code,
59
87
  client_id : fidor_config.client_id,
60
88
  client_secret : fidor_config.client_secret,
61
- redirect_uri : encodeURIComponent(fidor_config.app_url),
89
+ redirect_uri : encodeURIComponent(redirect_uri),
62
90
  grant_type : "authorization_code"
63
91
  }
64
92
  postData = querystring.stringify(postData)
65
93
 
66
94
  var http_module = oauth_url.protocol == "https:" ? https : http
95
+
67
96
  var token_request = http_module.request(postOptions, function (res) {
68
97
  // collect the data chunks we received and reassemble them
69
98
  // on request end ...
@@ -74,7 +103,6 @@ function retrieve_access_token_from_code( code, cb ) {
74
103
 
75
104
  res.on('end', function() {
76
105
  var oauth_response = JSON.parse(data)
77
- console.log(oauth_response)
78
106
  cb(oauth_response.error, oauth_response.access_token)
79
107
  })
80
108
  })
@@ -87,39 +115,217 @@ function retrieve_access_token_from_code( code, cb ) {
87
115
  token_request.end()
88
116
  }
89
117
 
118
+ /************************************************************************
119
+ /* END: OAuth Calls
120
+ ************************************************************************/
121
+
122
+
123
+ /************************************************************************
124
+ /* BEGIN: Cookie & Session Handling
125
+ ************************************************************************/
126
+ var COOKIE_NAME="NODE_SESSION"
127
+ var sessions = {}
128
+
129
+ // retrieve the random session_id from the cookie.
130
+ function getSession(req) {
131
+ var cookies = req.headers.cookie
132
+ var session = null
133
+ if (cookies) {
134
+ var cookieArr = cookies.split(";")
135
+ for (var i = 0 ; i != cookieArr.length ; ++i) {
136
+ var keyValue = cookieArr[i].split("=")
137
+ if (keyValue && keyValue.length == 2 && keyValue[0].trim() == COOKIE_NAME) {
138
+ session = keyValue[1]
139
+ break
140
+ }
141
+ }
142
+ }
143
+ return session
144
+ }
145
+ function getAccessTokenFromSession(req) {
146
+ var session = getSession(req)
147
+ var access_token = null
148
+ if (session) {
149
+ access_token = sessions[session]
150
+ }
151
+ return access_token
152
+ }
153
+ function createSession(resp, access_token) {
154
+ function randomString(len) {
155
+ var hex = "0123456789abcdef"
156
+ var rnd = ""
157
+ for (var i = 0; i!= len; ++i) {
158
+ rnd += hex[Math.floor(Math.random()*hex.length)]
159
+ }
160
+ return rnd
161
+ }
162
+ var rnd = randomString(20)
163
+ sessions[rnd] = access_token
164
+ resp.setHeader("Set-Cookie", COOKIE_NAME+"="+rnd)
165
+ }
166
+ function removeSession(req, resp) {
167
+ // find cookie
168
+ var session = getSession(req)
169
+ // delete from sessions
170
+ delete sessions[session]
171
+ // set expired
172
+ resp.setHeader("Set-Cookie", COOKIE_NAME+"=nothing; expires=Thu, 01 Jan 1970 00:00:00 GMT")
173
+ }
174
+
175
+ /************************************************************************
176
+ /* END: Cookie & Session Handling
177
+ ************************************************************************/
178
+
179
+ /************************************************************************
180
+ /* BEGIN: HTTP Handling
181
+ ************************************************************************/
90
182
 
183
+ //
91
184
  // Display a friendly message and links to the API Endpoints.
92
- function renderWelcome(request, response, token) {
185
+ //
186
+ function renderWelcome(request, response) {
93
187
  response.writeHead(200, {"Content-Type" : "text/html"})
94
- var content = hello_template.replace(/{token}/g, token)
95
- console.log(content)
96
- console.log(token)
97
- content = content.replace(/{api_uri}/g, fidor_config.fidor_api_url)
98
- response.end(content)
188
+ response.end(hello_template)
99
189
  }
100
190
 
101
- // main http functionality
102
- function listener (request, response) {
103
191
 
104
- var u = url.parse(request.url)
105
- // reject everything but GET.
106
- if (request.method !== "GET" || u.pathname !== "/") {
107
- response.writeHead(403, "Forbidden")
108
- response.end()
109
- return
192
+
193
+
194
+ function render (endpoint, req, res) {
195
+ //
196
+ // call the api endpoint and pipe the response from the API back to
197
+ // the caller
198
+ //
199
+ function pipeApi(endpoint, access_token, res) {
200
+ var api_endpoint = url.parse(fidor_config.fidor_api_url+endpoint)
201
+ var http_module = api_endpoint.protocol == "https:" ? https : http
202
+ var http_options = {
203
+ hostname: api_endpoint.hostname,
204
+ path: api_endpoint.path,
205
+ port: api_endpoint.port,
206
+ method: "GET",
207
+ headers: {
208
+ "Authorization": "Bearer "+access_token
209
+ }
210
+ }
211
+
212
+ var api_request = http_module.request(http_options, function(api_response) {
213
+ res.setHeader('Content-Type', api_response.headers['content-type'])
214
+ if (api_response.statusCode == 401) { // access_token has expired.
215
+ handleLogout(req, res, endpoint)
216
+ return
217
+ }
218
+ res.writeHead(api_response.statusCode, api_response.statusMessage)
219
+ api_response.on('data', function(chunk) {
220
+ res.write(chunk)
221
+ })
222
+ api_response.on('end', function(){
223
+ res.end()
224
+ })
225
+ })
226
+
227
+ api_request.on('error', function (err) {
228
+ res.writeHead(500, {'Content-Type': 'text/plain'})
229
+ res.end(err.toString())
230
+ })
231
+ api_request.end()
232
+ }
233
+
234
+ //
235
+ // utility to check whether access token is via session cookie, if not
236
+ // redirects account holder to OAuth Authorization Endpoint.
237
+ //
238
+ function getAccessToken(redirect, req, res, cb) {
239
+ var accesstoken = getAccessTokenFromSession(req)
240
+ if (!accesstoken) {
241
+ // start OAuth
242
+ redirect_to_oauth(res, redirect)
243
+ } else {
244
+ cb(null, accesstoken)
245
+ }
246
+
110
247
  }
111
- var code = querystring.parse(u.query)["code"]
112
- if (code) {
113
- retrieve_access_token_from_code( code, function (err, token) {
114
- if (err) {}
115
- renderWelcome(request, response, token)
248
+
249
+ getAccessToken(endpoint, req, res, function (err, accesstoken) {
250
+ if (err) {
251
+ res.writeHead(500, {'Content-Type': 'text/plain'})
252
+ res.end(err.toString())
253
+ return
254
+ }
255
+ pipeApi(endpoint, accesstoken, res)
256
+ })
257
+ }
258
+
259
+ function renderTransactions (req, res){
260
+ render("/transactions", req, res)
261
+ }
262
+ function renderAccounts (req, res){
263
+ render("/accounts", req, res)
264
+ }
265
+
266
+ //
267
+ // retrieve the 'code' parameter from the redirect url the
268
+ // OAuth Authorization returns to the user's browser
269
+ //
270
+ function handleOAuthCallback(req, res) {
271
+ var u = url.parse(req.url)
272
+ var query = querystring.parse(u.query)
273
+ var code = query["code"]
274
+ var target = query["ep"]
275
+
276
+ if (code && target) {
277
+ retrieve_access_token_from_code( code, target, function (err, token) {
278
+ if (err) {
279
+ res.writeHead(500, {'Content-Type': 'text/plain'})
280
+ res.end(err.toString())
281
+ return
282
+ }
283
+ createSession(res, token)
284
+ res.writeHead(307, {"location" : target})
285
+ res.end()
116
286
  })
117
287
  } else {
118
- // we don't have an oauth `code` yet, so we need to
119
- // redirect the user to the OAuth provider to get one ...
120
- redirect_to_oauth(response)
288
+ res.writeHead(500, {'Content-Type': 'text/plain'})
289
+ res.end("missing code or target")
290
+ }
291
+ }
292
+
293
+ function handleLogout(req, res, endpoint) {
294
+ // clear session locally and in browser
295
+ removeSession(req, res)
296
+ res.writeHead(307, {"location" : endpoint})
297
+ res.end()
298
+ }
299
+
300
+ function listener (request, response) {
301
+ var u = url.parse(request.url)
302
+
303
+ if (request.method !== "GET") {
304
+ response.writeHead(403, "Forbidden")
305
+ response.end()
121
306
  return
122
307
  }
308
+
309
+ switch(u.pathname) {
310
+ case "/":
311
+ renderWelcome(request, response)
312
+ break
313
+ case "/transactions":
314
+ renderTransactions(request, response)
315
+ break
316
+ case "/accounts":
317
+ renderAccounts(request, response)
318
+ break
319
+ case "/oauth":
320
+ handleOAuthCallback(request, response)
321
+ break
322
+ case "/logout":
323
+ handleLogout(request, response, "/")
324
+ break
325
+ default:
326
+ response.writeHead(404, "Not Found")
327
+ }
328
+
123
329
  }
124
330
 
125
331
  // Execution starts here...
@@ -127,7 +333,7 @@ function listener (request, response) {
127
333
  var url = require("url")
128
334
  var querystring = require("querystring")
129
335
  var http = require("http")
130
- var https = require("http")
336
+ var https = require("https")
131
337
 
132
338
  var _url = url.parse(fidor_config.app_url)
133
339
  fidor_config.app_port = _url.port
@@ -145,8 +351,8 @@ var hello_template = ""+
145
351
  "</head>"+
146
352
  "<body>"+
147
353
  " <h1>Welcome!</h1>"+
148
- " <i>retrieved <tt>access_token</tt>: {token} </i>"+
149
- " <p><a href='{api_uri}/transactions?access_token={token}'>Transactions</a></p>"+
150
- " <p><a href='{api_uri}/accounts?access_token={token}'>Accounts</a></p>"+
354
+ " <p><a href='/transactions'>Transactions</a></p>"+
355
+ " <p><a href='/accounts'>Accounts</a></p>"+
356
+ " <p><a href='/logout'>Log Out</a></p>"+
151
357
  "</body>"+
152
358
  "</html>"
@@ -1,7 +1,7 @@
1
1
  {
2
- "display_name" : "PHP Plain",
2
+ "display_name" : "PHP oAuth Plain",
3
3
  "description" : "A simple php script, showing how to get the access token",
4
- "app_name":"php_plain",
4
+ "app_name":"php_oauth_plain",
5
5
  "app_url":"http://localhost:8000/example.php",
6
6
  "callback_urls":"http://localhost:8000/example.php"
7
7
  }
@@ -1,4 +1,4 @@
1
- # Plain PHP Login - Example
1
+ # Plain PHP oAuth Example
2
2
 
3
3
  A single view that demonstrates the Fidor API OAuth login flow and how to get
4
4
  an access token.
@@ -1,12 +1,13 @@
1
1
  <?php
2
2
 
3
- $app_url = "<APP_URL>"; # default http://localhost:8000/example.php
4
- $app_id = "<CLIENT_ID>";
5
- $app_secret = "<CLIENT_SECRET>";
6
- $fidor_oauth_url = "<FIDOR_OAUTH_URL>"; # e.g https://fidor.com/api_sandbox/oauth
7
- $fidor_api_url = "<FIDOR_API_URL>"; # e.g https://fidor.com/api_sandbox vs /api
3
+ $app_url = "<APP_URL>"; # default http://localhost:8000/example.php
4
+ $app_id = "<CLIENT_ID>";
5
+ $app_secret = "<CLIENT_SECRET>";
6
+ $fidor_oauth_url= "<FIDOR_OAUTH_URL>"; # e.g Sandbox: https://aps.fidor.de/oauth / Live: https://apm.fidor.de/oauth
7
+ $fidor_api_url = "<FIDOR_API_URL>"; # e.g Sandbox: https://aps.fidor.de / Live: https://api.fidor.de
8
8
 
9
- $code = $_REQUEST["code"];
9
+
10
+ $code = $_REQUEST["code"];
10
11
 
11
12
  # 1. redirect to authorize url
12
13
  if(empty($code)) {
@@ -38,15 +39,13 @@
38
39
  $context = stream_context_create($options);
39
40
  $resp = json_decode(file_get_contents($token_url, false, $context));
40
41
 
41
- # 3. GET Data e.g.info about current user
42
- $usr_url = $fidor_api_url . "/users/current?access_token=" . $resp->access_token;
43
- $user = json_decode(file_get_contents($usr_url));
44
- $transactions_url = $fidor_api_url . "/transactions?access_token=" . $resp->access_token;
45
- echo( "<h2>Hello " . $user->email . "</h2>
42
+ echo( "<h2>Hello</h2>
46
43
  <i>May I present the access token response:</i>
47
44
  <blockquote>");
48
45
  print_r($resp);
49
46
  echo("</blockquote>
50
- <p>Now use the access token here: <br> <a href='" . $transactions_url . "'>".$transactions_url."</a></p>");
47
+ <p>Now use the access token in the request header in your favorite PHP HTTP method or via CURL: </p>
48
+ <blockquote>curl -v -H \"Authorization: Bearer ".$resp->access_token."\" ".$fidor_api_url."/transactions
49
+ </blockquote>");
51
50
 
52
- ?>
51
+ ?>
@@ -0,0 +1,7 @@
1
+ {
2
+ "display_name" : "Ruby oAuth Plain",
3
+ "description" : "A simple Ruby app showing the oAuth dance to get and use the access token. Uses Sinatra as server.",
4
+ "app_name":"ruby_oauth_plain",
5
+ "app_url":"http://localhost:4567",
6
+ "callback_urls":"http://localhost:4567"
7
+ }
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
- gem 'activesupport'
3
2
  gem 'sinatra'
4
- gem 'curb'
3
+ gem 'httparty'
@@ -1,13 +1,13 @@
1
1
  # Ruby Plain oAuth Login - Example
2
2
 
3
3
  A single view that demonstrates the Fidor API OAuth login flow and how to get
4
- an access token.
4
+ an access token. Uses Sinatra on its server side.
5
5
 
6
6
  ## Usage
7
7
 
8
8
  This uses bundler to install the required gems:
9
9
 
10
- cd sinatra_plain
10
+ cd ruby_oauth_plain
11
11
  bundle install
12
12
  ruby example.rb
13
13
 
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'httparty'
4
+
5
+ get '/' do
6
+ # settings
7
+ @app_url = '<APP_URL>' # default for local installs: http://localhost:4567
8
+ @client_id = '<CLIENT_ID>'
9
+ @client_secret = '<CLIENT_SECRET>'
10
+ @fidor_oauth_url = '<FIDOR_OAUTH_URL>' # e.g Sandbox: https://aps.fidor.de/oauth / Live: https://apm.fidor.de/oauth
11
+ @fidor_api_url = '<FIDOR_API_URL>' # e.g Sandbox: https://aps.fidor.de / Live: https://api.fidor.de
12
+
13
+ # 1. redirect to authorize url
14
+ unless code = params["code"]
15
+ dialog_url = "#{@fidor_oauth_url}/authorize?client_id=#{@client_id}&redirect_uri=#{CGI::escape(@app_url)}&state=1234&response_type=code"
16
+ redirect dialog_url
17
+ end
18
+
19
+ # 2. get the access token, with code returned from auth dialog above
20
+ token_url = URI("#{@fidor_oauth_url}/token")
21
+ post_params = { client_id: @client_id,
22
+ redirect_uri: CGI::escape(@app_url),
23
+ code: code,
24
+ client_secret: @client_secret,
25
+ grant_type: 'authorization_code' }
26
+ resp = HTTParty.post(token_url, body: post_params )
27
+
28
+ # GET current user setting the access-token in the request header
29
+ user = HTTParty.get( "#{@fidor_api_url}/users/current",
30
+ headers: { 'Authorization' => "Bearer #{resp['access_token']}"} )
31
+
32
+ "<h2>Hello #{user['email']}</h2>
33
+ <i>May i present the access token response:</i>
34
+ <blockquote>#{resp.body}</blockquote>
35
+ <p>Now use the access token in the Header of your Requests, e.g. using CURL</p>
36
+ <blockquote>
37
+ curl -v -H \"Authorization: Bearer #{resp['access_token']}\" #{@fidor_api_url}/accounts
38
+ </blockquote>"
39
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fidor_starter_kits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Georg Leciejewski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2015-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -109,13 +109,13 @@ files:
109
109
  - starter_kits/java_servlet/src/de/fidor/api/example/Example.java
110
110
  - starter_kits/node_tx/.fidor_meta.json
111
111
  - starter_kits/node_tx/example.js
112
- - starter_kits/php_plain/.fidor_meta.json
113
- - starter_kits/php_plain/README.md
114
- - starter_kits/php_plain/example.php
115
- - starter_kits/sinatra_plain/.fidor_meta.json
116
- - starter_kits/sinatra_plain/Gemfile
117
- - starter_kits/sinatra_plain/README.md
118
- - starter_kits/sinatra_plain/example.rb
112
+ - starter_kits/php_oauth_plain/.fidor_meta.json
113
+ - starter_kits/php_oauth_plain/README.md
114
+ - starter_kits/php_oauth_plain/example.php
115
+ - starter_kits/ruby_oauth_plain/.fidor_meta.json
116
+ - starter_kits/ruby_oauth_plain/Gemfile
117
+ - starter_kits/ruby_oauth_plain/README.md
118
+ - starter_kits/ruby_oauth_plain/example.rb
119
119
  homepage: ''
120
120
  licenses:
121
121
  - MIT
@@ -1,7 +0,0 @@
1
- {
2
- "display_name" : "Ruby Sinatra Plain",
3
- "description" : "A simple Sinatra based app, showing how to get an access token.",
4
- "app_name":"sinatra_plain",
5
- "app_url":"http://localhost:4567",
6
- "callback_urls":"http://localhost:4567"
7
- }
@@ -1,40 +0,0 @@
1
- require 'rubygems'
2
- require 'sinatra'
3
- require 'active_support/json'
4
- require 'net/http'
5
-
6
- get '/' do
7
- # settings
8
- @app_url = '<APP_URL>' # default for local installs: http://localhost:4567
9
- @client_id = '<CLIENT_ID>'
10
- @client_secret = '<CLIENT_SECRET>'
11
- @fidor_oauth_url = '<FIDOR_OAUTH_URL>' # e.g https://fidor.com/oauth
12
- @fidor_api_url = '<FIDOR_API_URL>' # e.g https://fidor.com/api_sandbox, https://fidor.com/api
13
-
14
- # 1. redirect to authorize url
15
- unless code = params["code"]
16
- dialog_url = "#{@fidor_oauth_url}/authorize?client_id=#{@client_id}&redirect_uri=#{CGI::escape(@app_url)}&state=1234&response_type=code"
17
- redirect dialog_url
18
- end
19
-
20
- # 2. get the access token, with code returned from auth dialog above
21
- token_url = URI("#{@fidor_oauth_url}/token")
22
- # GET and parse access_token response json
23
- res = Net::HTTP.post_form(token_url, 'client_id' => @client_id,
24
- 'redirect_uri' => CGI::escape(@app_url),
25
- 'code' =>code,
26
- 'client_secret'=>@client_secret,
27
- 'grant_type'=>'authorization_code')
28
-
29
- resp = ActiveSupport::JSON.decode(res.body)
30
-
31
- # GET current user
32
- usr_url = "#{@fidor_api_url}/users/current?access_token=#{resp['access_token']}"
33
- user = ActiveSupport::JSON.decode( Net::HTTP.get URI(usr_url) )
34
- account_url = "#{@fidor_api_url}/accounts?access_token=#{resp['access_token']}"
35
- "<h2>Hello #{user['email']}</h2>
36
- <i>May i present the access token response:</i>
37
- <blockquote>#{resp.inspect}</blockquote>
38
- <p>Now use the access token in <br> <a href='#{account_url}'>#{account_url}</a></p>
39
- "
40
- end