forked from adobe/ims-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1f39dcd
commit 5e54733
Showing
11 changed files
with
1,081 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2019 Adobe. All rights reserved. | ||
// This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. You may obtain a copy | ||
// of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
// OF ANY KIND, either express or implied. See the License for the specific language | ||
// governing permissions and limitations under the License. | ||
|
||
package login | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/adobe/ims-go/ims" | ||
) | ||
|
||
type callbackBackend interface { | ||
Token(r *ims.TokenRequest) (*ims.TokenResponse, error) | ||
} | ||
|
||
type callbackMiddleware struct { | ||
client callbackBackend | ||
state string | ||
clientID string | ||
clientSecret string | ||
scope []string | ||
next http.Handler | ||
} | ||
|
||
func (h *callbackMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
values := r.URL.Query() | ||
|
||
uerr := values.Get("error") | ||
if uerr != "" { | ||
serveError(h.next, w, r, fmt.Errorf("backend error: %s", uerr)) | ||
return | ||
} | ||
|
||
state := values.Get("state") | ||
if state == "" { | ||
serveError(h.next, w, r, fmt.Errorf("missing state parameter")) | ||
return | ||
} | ||
|
||
if h.state != state { | ||
serveError(h.next, w, r, fmt.Errorf("invalid state parameter")) | ||
return | ||
} | ||
|
||
code := values.Get("code") | ||
if code == "" { | ||
serveError(h.next, w, r, fmt.Errorf("missing code parameter")) | ||
return | ||
} | ||
|
||
res, err := h.client.Token(&ims.TokenRequest{ | ||
Code: code, | ||
ClientID: h.clientID, | ||
ClientSecret: h.clientSecret, | ||
Scope: h.scope, | ||
}) | ||
if err != nil { | ||
serveError(h.next, w, r, fmt.Errorf("obtaining access token: %v", err)) | ||
return | ||
} | ||
|
||
serveResult(h.next, w, r, res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
// Copyright 2019 Adobe. All rights reserved. | ||
// This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. You may obtain a copy | ||
// of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
// OF ANY KIND, either express or implied. See the License for the specific language | ||
// governing permissions and limitations under the License. | ||
|
||
package login | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
"testing" | ||
|
||
"github.com/adobe/ims-go/ims" | ||
) | ||
|
||
type testCallbackBackend func(r *ims.TokenRequest) (*ims.TokenResponse, error) | ||
|
||
func (b testCallbackBackend) Token(r *ims.TokenRequest) (*ims.TokenResponse, error) { | ||
return b(r) | ||
} | ||
|
||
func urlWithParams(path string, params map[string]string) string { | ||
values := url.Values{} | ||
|
||
for k, v := range params { | ||
values.Set(k, v) | ||
} | ||
|
||
u := url.URL{ | ||
Path: path, | ||
RawQuery: values.Encode(), | ||
} | ||
|
||
return u.String() | ||
} | ||
|
||
func TestCallback(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
state: "state", | ||
clientID: "client-id", | ||
clientSecret: "client-secret", | ||
scope: []string{"a", "b"}, | ||
|
||
client: testCallbackBackend(func(r *ims.TokenRequest) (*ims.TokenResponse, error) { | ||
if r.Code != "code" { | ||
t.Fatalf("invalid code: %v", r.Code) | ||
} | ||
if r.ClientID != "client-id" { | ||
t.Fatalf("invalid client ID: %v", r.ClientID) | ||
} | ||
if r.ClientSecret != "client-secret" { | ||
t.Fatalf("invalid client secret: %v", r.ClientSecret) | ||
} | ||
if len(r.Scope) != 2 && r.Scope[0] != "a" && r.Scope[1] != "b" { | ||
t.Fatalf("invalid scope: %v", r.Scope) | ||
} | ||
return &ims.TokenResponse{}, nil | ||
}), | ||
|
||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
res, ok := r.Context().Value(contextKeyResult).(*ims.TokenResponse) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if res == nil { | ||
t.Fatalf("no response returned") | ||
} | ||
}), | ||
} | ||
|
||
target := urlWithParams("/", map[string]string{ | ||
"code": "code", | ||
"state": "state", | ||
}) | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, target, nil)) | ||
} | ||
|
||
func TestCallbackError(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
err, ok := r.Context().Value(contextKeyError).(error) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if err == nil { | ||
t.Fatalf("no error returned") | ||
} | ||
if err.Error() != "backend error: error" { | ||
t.Fatalf("invalid error: %v", err) | ||
} | ||
}), | ||
} | ||
|
||
target := urlWithParams("/", map[string]string{ | ||
"error": "error", | ||
}) | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, target, nil)) | ||
} | ||
|
||
func TestCallbackNoState(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
err, ok := r.Context().Value(contextKeyError).(error) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if err == nil { | ||
t.Fatalf("no error returned") | ||
} | ||
if err.Error() != "missing state parameter" { | ||
t.Fatalf("invalid error: %v", err) | ||
} | ||
}), | ||
} | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, "/", nil)) | ||
} | ||
|
||
func TestCallbackInvalidState(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
state: "state", | ||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
err, ok := r.Context().Value(contextKeyError).(error) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if err == nil { | ||
t.Fatalf("no error returned") | ||
} | ||
if err.Error() != "invalid state parameter" { | ||
t.Fatalf("invalid error: %v", err) | ||
} | ||
}), | ||
} | ||
|
||
target := urlWithParams("/", map[string]string{ | ||
"state": "invalid-state", | ||
}) | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, target, nil)) | ||
} | ||
|
||
func TestCallbackMissingCode(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
state: "state", | ||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
err, ok := r.Context().Value(contextKeyError).(error) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if err == nil { | ||
t.Fatalf("no error returned") | ||
} | ||
if err.Error() != "missing code parameter" { | ||
t.Fatalf("invalid error: %v", err) | ||
} | ||
}), | ||
} | ||
|
||
target := urlWithParams("/", map[string]string{ | ||
"state": "state", | ||
}) | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, target, nil)) | ||
} | ||
|
||
func TestCallbackBackendError(t *testing.T) { | ||
m := &callbackMiddleware{ | ||
state: "state", | ||
client: testCallbackBackend(func(r *ims.TokenRequest) (*ims.TokenResponse, error) { | ||
return nil, fmt.Errorf("error") | ||
}), | ||
next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
err, ok := r.Context().Value(contextKeyError).(error) | ||
if !ok { | ||
t.Fatalf("invalid context value") | ||
} | ||
if err == nil { | ||
t.Fatalf("no error returned") | ||
} | ||
if err.Error() != "obtaining access token: error" { | ||
t.Fatalf("invalid error: %v", err) | ||
} | ||
}), | ||
} | ||
|
||
target := urlWithParams("/", map[string]string{ | ||
"state": "state", | ||
"code": "code", | ||
}) | ||
|
||
m.ServeHTTP(nil, httptest.NewRequest(http.MethodGet, target, nil)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// ADOBE CONFIDENTIAL | ||
// ___________________ | ||
// | ||
// Copyright 2019 Adobe Systems Incorporated | ||
// All Rights Reserved. | ||
// | ||
// NOTICE: All information contained herein is, and remains the property of | ||
// Adobe Systems Incorporated and its suppliers, if any. The intellectual and | ||
// technical concepts contained herein are proprietary to Adobe Systems | ||
// Incorporated and its suppliers and are protected by trade secret or copyright | ||
// law. Dissemination of this information or reproduction of this material is | ||
// strictly forbidden unless prior written permission is obtained from Adobe | ||
// Systems Incorporated. | ||
|
||
package login | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
|
||
"github.com/adobe/ims-go/ims" | ||
) | ||
|
||
type contextKey string | ||
|
||
var ( | ||
contextKeyError = contextKey("error") | ||
contextKeyResult = contextKey("result") | ||
) | ||
|
||
func serveError(h http.Handler, w http.ResponseWriter, r *http.Request, err error) { | ||
h.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKeyError, err))) | ||
} | ||
|
||
func serveResult(h http.Handler, w http.ResponseWriter, r *http.Request, res *ims.TokenResponse) { | ||
h.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKeyResult, res))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright 2019 Adobe. All rights reserved. | ||
// This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. You may obtain a copy | ||
// of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
// OF ANY KIND, either express or implied. See the License for the specific language | ||
// governing permissions and limitations under the License. | ||
|
||
package login | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/adobe/ims-go/ims" | ||
) | ||
|
||
type redirectBackend interface { | ||
AuthorizeURL(cfg *ims.AuthorizeURLConfig) (string, error) | ||
} | ||
|
||
type redirectMiddleware struct { | ||
client redirectBackend | ||
clientID string | ||
scope []string | ||
state string | ||
next http.Handler | ||
} | ||
|
||
func (h *redirectMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
url, err := h.client.AuthorizeURL(&ims.AuthorizeURLConfig{ | ||
ClientID: h.clientID, | ||
Scope: h.scope, | ||
State: h.state, | ||
}) | ||
if err != nil { | ||
serveError(h.next, w, r, fmt.Errorf("generate authorization URL: %v", err)) | ||
return | ||
} | ||
|
||
http.Redirect(w, r, url, http.StatusFound) | ||
} |
Oops, something went wrong.