I'm kinda of new in EG. I have followed the documentation about issuing a jwt token instead of opaque but still receiving an opaque access token. Not sure what I'm missing to change.
This is my system.config file
db:
redis:
host: localhost
port: 6379
namespace: EG
crypto:
cipherKey: sensitiveKey
algorithm: aes256
saltRounds: 10
session:
secret: keyboard cat
resave: false
saveUninitialized: false
accessTokens:
timeToExpiry: 7200000
tokenType: 'jwt'
issuer: 'express-gateway'
audience: 'something'
subject: 'test'
secretOrPrivateKey: 'ssssst'
refreshTokens:
timeToExpiry: 7200000
authorizationCodes:
timeToExpiry: 300000
Already added a user and app with their own credentials (oauth2, basic-auth, jwt) without changing anything on the models.
this is my gateway.config file
http:
port: 8080
admin:
port: 9876
hostname: localhost
apiEndpoints:
api:
host: localhost
paths: '/api/*'
serviceEndpoints:
httpbin:
url: 'https://httpbin.org'
policies:
- jwt
- oauth2
- proxy
- rate-limit
pipelines:
default:
apiEndpoints:
- api
policies:
- oauth2:
action:
jwt:
issuer: express-gateway
audience: something
subject: test
secretOrPublicKey: ssssst
checkCredentialExistence: false
- proxy:
- action:
serviceEndpoint: httpbin
changeOrigin: true
the request for login is like this and secret is the keySecret generated with jwt credential of the app.
http://localhost:8080/oauth2/authorize?response_type=token&client_id=ae921ba9-7b4b-4c53-aaba-354bd6398e52&redirect_uri=http://localhost:3002/explorer&client_secret=0qOpBZkwO2ayQ8dO18yRuh
and the token response looks like this.
14070f7c4ffc49efb1fc1709cc4a7267|90c6efd2cf8342859756d8e3705417a3
Thanks for anyone who can help me.
The configuration looks correct. I've also personally tried the configuration you provided and I got back a JWT
Maybe you can try to set up a test project on Glitch.me so it's going to be easier to replicate locally.
Related
I have two services and a gateway working with eureka:
user-service running on 8081
health-service running on 8082
So far I tried this:
routes:
- id: user-service-route
uri: http://localhost:8081/user-service
predicates:
- Path=/user-service/**
filters:
- RewritePath=(?<serviceName>.*), /api/v1/{serviceName}
But it doesn't work.
try
filters:
- RewritePath=(?<serviceName>.*), /api/v1/$\{serviceName}
Prefix location for all microservices if you use load balancer:
server:
port: 8765
management:
endpoints:
web:
exposure:
include: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
default-filters:
- RewritePath=/api/v1/.*?/(?<remaining>.*), /$\{remaining}
routes:
- predicates:
- Path=/api/v1/profile/**
uri: lb://profile
- predicates:
- Path=/api/v1/dictionary/**
uri: lb://dictionary
I am trying to figure out how to get Express Gateway to use the Auth Bearer token in request, upstream to the api service.
Below is my config as of right now.
http:
port: 8080
admin:
port: 9876
host: localhost
apiEndpoints:
api:
host: localhost
paths:
- '/truck-api/*'
- '/car-api/*'
serviceEndpoints:
truck-service:
url: 'http://10.0.0.2:5010/api'
car-service:
url: 'http://10.0.0.2:5011/api'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
- rewrite
- request-transformer
pipelines:
default:
apiEndpoints:
- api
policies:
- proxy:
-
condition:
name: pathMatch
pattern: "^/truck-api/(.*)"
action:
serviceEndpoint: truck-service
prependPath: true
ignorePath: false
stripPath: true
changeOrigin: true
-
condition:
name: pathMatch
pattern: "^/car-api/(.*)"
action:
serviceEndpoint: car-service
prependPath: true
ignorePath: false
stripPath: true
changeOrigin: true
The frontend calls express gateway with a proper auth bearer token, however the express gateway doesn't forward that auth bearer token on to the upstream service.
Advice?
After some digging and a lot of trial and error the solution was relatively simple, use request transformer.
added this to the policies section:
- request-transformer:
- action:
headers:
remove: ['Authorization']
add:
Authorization: "'Bearer ***'"
For me remove: ['Authorization'] did not work.
I was authenticating towards express gateway using:
"Authorization: apiKey ${keyId}:${keySecret}" and authenticating towards backend service by using Authorization: "'Bearer ***'" added by request-transformer. I would expect remove: ['Authorization'] would remove "Authorization: apiKey ${keyId}:${keySecret}", after I was already successfully authenticated at express gateway and add: Authorization: "'Bearer ***'" would add new Authorization header so newly constructed request would replace apiKey with Bearer token but using remove: ['Authorization'] resulted in failure to authenticate at express gateway.
I resolved this issue by changing HTTP header used for authorization at express gateway side with apiKeyHeader (https://www.express-gateway.io/docs/policies/key-authorization/).
policies:
- request-transformer:
- action:
headers:
add:
# pass oauth2 token
Authorization: "'Bearer ***'"
- key-auth:
- action:
# arbitrarily rename default header name from Authorization
apiKeyHeader: open_sesame
The open_sesame header grants us access to api gateway and request-transformer adds authorization header with proper token to the request for accessing the backend service.
curl -v -H "open_sesame: apiKey ${keyId}:${keySecret}"...
I want to force the add of a filed in the req.body, according to the scope of the credentials. I have 2 Apps (App1 and App2), and based on who is using my API, I want to programmatically add a field in the req. So credentials of App1 has scope app1, and app2 in App2's scopes.
Moreover, I have 2 Environments, with different endpoints. Both App has access to both Ends (using different credentials). So I first choose the Env (using dev_env or my_env scope), then I verify which App is accessing (checking app1 or app2 scope).
To do that, I use expression apiEndpoint.scopes.indexOf('app1')>=0. that actually is not working, since the condition is always false. So for debugging purpose, I put the content of apiEndpoint.scopes as additional field in the req.body, to see what there is in that.
And I see that apiEndpoint.scopes has just ["my_env"], not "app1". Why?
So I have
http:
port: ${PORT:-8080}
host: ${HOST:-localhost}
apiEndpoints:
myEndpoint:
host: "*"
scopes: ["my_env"] # I explain just this one here
devEndpoint:
host: "*"
scopes: ["dev_env"]
serviceEndpoints:
myEndpoint:
url: 'https://myserver'
policies:
- basic-auth
- cors
- expression
- key-auth
- request-transformer
- rewrite
- oauth2
- proxy
- rate-limit
pipelines:
myEndpoint:
apiEndpoints:
- myEndpoint
policies:
- request-transformer:
-
condition:
name: allOf
conditions:
- # check if scope 'app1' is present. expression not working
#name: expression
#expression: "apiEndpoint.scopes.indexOf('app1')>=0"
action:
body:
add:
available_scopes: "apiEndpoint.scopes" # debug of available scopes.
And the content of req.body is
{"available_scopes": ["my_env"]}
'app1' is missing!
==== update 1
If in req.body.available_scopes field I put "consumer", I got this:
{
"type": "application",
"isActive": true,
"id": "....",
"userId": "...",
"name": "...",
"company": "...",
"authorizedScopes": [
"my_env"
]
}
So it talks about "authorizedScopes", where are the others? How could I see them?
Thanks
You have specified the scopes my_env and dev_env for the apiEndpoints myEndpoint and devEndpoint (respectively), and these are the only scopes Express Gateway expects you to care about, so the other scopes associated with the user/app credential are not exposed.
You could add the app1 and app2 scopes to each path in the config file and then act based on whichever scope is set for the credentials of the connecting app:
apiEndpoints:
myEndpoint:
host: "*"
scopes: ["my_env","app1","app2"]
devEndpoint:
host: "*"
scopes: ["dev_env","app1","app2"]
I added login: Admin to app.yaml like documentation is described, but I still have a 302 erro when runing a task with cron in google app engine.
I have this app.yaml
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: main.app
login: admin
And this cron.yaml
cron:
- description: "Dashboard"
url: /processdate?from=2016-03-01&until=2016-03-31
schedule: every day 23:46
timezone: Europe/Madrid
I obtain this error
0.1.0.1 - - [26/Oct/2018:00:49:40 +0200] "GET /processdate?from=2016-03-01&until=2016-03-31 HTTP/1.1" 302 355 - "AppEngine-Google; (+http://code.google.com/appengine)" "p20000.appspot.com" ms=74 cpu_ms=12 cpm_usd=3.9674e-8 loading_request=0 instance=00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a app_engine_release=1.9.65 trace_id=3c92edad090b5a57d249bd92be246e58
httpRequest: {
status: 302
}
insertId: "5bd248840005a3aae7fa2111"
labels: {
clone_id: "00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a"
}
logName: "projects/p201309/logs/appengine.googleapis.com%2Frequest_log"
operation: {
first: true
id: "5bd2488400ff047fe69ec5d94d0001657e62692d70682d3230313330390001323031383130323674303033383339000100"
last: true
producer: "appengine.googleapis.com/request_id"
}
protoPayload: {
#type: "type.googleapis.com/google.appengine.logging.v1.RequestLog"
appEngineRelease: "1.9.65"
appId: "e~myappname"
cost: 3.9674e-8
endTime: "2018-10-25T22:49:40.369327Z"
finished: true
first: true
host: "p200000.appspot.com"
httpVersion: "HTTP/1.1"
instanceId: "00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a"
instanceIndex: -1
ip: "0.1.0.1"
latency: "0.074441s"
megaCycles: "12"
method: "GET"
requestId: "5bd2488400ff047fe69ec5d94d0001657e62692d70682d3230313330390001323031383130323674303033383339000100"
resource: "/processdate?from=2016-03-01&until=2016-03-31"
responseSize: "355"
startTime: "2018-10-25T22:49:40.294886Z"
status: 302
taskName: "25ed634cde05b07d9a7906f2161d2b16"
taskQueueName: "__cron"
traceId: "3c92edad090b5a57d249bd92be246e58"
traceSampled: true
urlMapEntry: "main.app"
userAgent: "AppEngine-Google; (+http://code.google.com/appengine)"
versionId: "20181026t003839"
}
receiveTimestamp: "2018-10-25T22:49:40.376251430Z"
resource: {
labels: {
module_id: "default"
project_id: "myappname"
version_id: "20181026t003839"
zone: "eu2"
}
type: "gae_app"
}
timestamp: "2018-10-25T22:49:40.294886Z"
trace: "projects/myappname/traces/3c92edad090b5a57d249bd92be246e58"
traceSampled: true
}
Any idea about what could be the problem or how to solve it?
I don't have any problem runing this script in my local machine.
------ UPDATED WITH A MUCH MORE SIMPLE APP ------
I tested something much more simple. This is working when I run https://myappname.appspot.com/hellocron and http://myappname.appspot.com/hellocron
With this the result it's what expected.
But when I run it with cron error 302 it's returned again
----------- app.yaml
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /hellocron
script: main.app
login: admin
secure: always
----------- cron.yaml
cron:
- description: "hellocron"
url: /hellocron
schedule: every day 23:46
timezone: Europe/Madrid
------------ I had the same error result
0.1.0.1 - - [01/Nov/2018:12:29:49 +0100] "GET /hellocron HTTP/1.1" 302 267 - "AppEngine-Google; (+http://code.google.com/appengine)" "myappname.appspot.com" ms=8 cpu_ms=9 cpm_usd=2.9839e-8 loading_request=0 instance=00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27 app_engine_release=1.9.65 trace_id=9b9fa8ad127ad41f7907529c3863a0a9
{
httpRequest: {
status: 302
}
insertId: "5bdae3ad0004cfe52d64d457"
labels: {
clone_id: "00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27"
}
logName: "projects/myappname/logs/appengine.googleapis.com%2Frequest_log"
operation: {
first: true
id: "5bdae3ad00ff04ac4decaecea60001657e62692d70682d3230313330390001323031383131303174313135363535000100"
last: true
producer: "appengine.googleapis.com/request_id"
}
protoPayload: {
#type: "type.googleapis.com/google.appengine.logging.v1.RequestLog"
appEngineRelease: "1.9.65"
appId: "e~myappname"
cost: 2.9839e-8
endTime: "2018-11-01T11:29:49.315161Z"
finished: true
first: true
host: "myappname.appspot.com"
httpVersion: "HTTP/1.1"
instanceId: "00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27"
instanceIndex: -1
ip: "0.1.0.1"
latency: "0.008908s"
megaCycles: "9"
method: "GET"
requestId: "5bdae3ad00ff04ac4decaecea60001657e62692d70682d3230313330390001323031383131303174313135363535000100"
resource: "/hellocron"
responseSize: "267"
startTime: "2018-11-01T11:29:49.306253Z"
status: 302
taskName: "b0467e8a57f53a8ee2b827ca35db275f"
taskQueueName: "__cron"
traceId: "9b9fa8ad127ad41f7907529c3863a0a9"
traceSampled: true
urlMapEntry: "main.app"
userAgent: "AppEngine-Google; (+http://code.google.com/appengine)"
versionId: "20181101t115655"
}
receiveTimestamp: "2018-11-01T11:29:49.321937019Z"
resource: {
labels: {
module_id: "default"
project_id: "myappname"
version_id: "20181101t115655"
zone: "eu2"
}
type: "gae_app"
}
timestamp: "2018-11-01T11:29:49.306253Z"
trace: "projects/myappname/traces/9b9fa8ad127ad41f7907529c3863a0a9"
traceSampled: true
}
The code for /hellocron in python is this one basically:
decorator = OAuth2DecoratorFromClientSecrets(
os.path.join(os.path.dirname(__file__), 'client_secrets.json'),
scope='https://www.googleapis.com/auth/bigquery')
class hellocron (webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
self.response.write('hellocron')
app = webapp2.WSGIApplication([
('/hellocron', hellocron),
(decorator.callback_path, decorator.callback_handler()) ], debug=True)
You want to drop the #decorator.oauth_required from the cron url handler code.
The cron service does not have any user credentials (it doesn't run as a user) so that decorator will cause a re-direction to a login service - hence the 302 response. You should be able to verify this by re-trying your manual check but from an incognito browser window.
To secure the cron service URLs you can't use regular user authentication for this reason. But you can use login: admin in the app.yaml file and, if you want, also check for the X-Appengine-Cron: true header or the 0.1.0.1 source IP address, see Securing URLs for cron.
Similarly you might need to drop the secure: always from the respective app.yaml handler definition (I don't have it enabled for my app): I'm not sure if the cron service makes its GET request using http or https. If it makes it using http the secure: always config will also cause a redirection to a https URL. You can easily check if this is correct or not following my earlier comment after you drop the decorator.
My name is Dan I'm from google cloud support.
As I could understand, you're receiving a 302 response when launching Cron jobs.
Have you a custom domain with SSL enabled? I ask you this because we have received this report from some customers that are using HTTP instead of HTTPS.
Are you running your application on App Engine flexible? This information will help me to better understand your current scenario.
I will be awaiting your reply.
I wanted to post this solution as I was pulling my hair out yesterday trying to figure out why my GAE cron jobs kept failing and this thread helped me fine tune the solution that worked.
I am using Python, but the idea seems the same. In my cron script (i.e. the page visited with the cron logic), I had a redirect after successful execution of a report, due to this the cron would state it failed in GAE, but the logic would still execute correctly.
To fix the issue, all I did was return a simple HTTP response so the cron thought everything loaded correctly on the page instead of the redirect or not returning an HTTP response all together.
I was trying to execute a django admin endpoint from gcloud app engine. It seems there is no way to make gcloud crons login with any credentials before running so here is what you have to do if you find yourself in this situation (receiving 302 in your crons executions):
Make the url public, that means: no credentials needed to run it.
Gcloud crons send request including X-AppEngine-Cron in it's headers. They strip 'X-' from all their headers so you may trust this particular one. All you have to do is check for this header in your endpoint request as this guy does.
Doc: https://cloud.google.com/appengine/docs/flexible/go/scheduling-jobs-with-cron-yaml?hl=es-419#validating_cron_requests
I would like to document and test an API, which uses Cookie-based authetication in http://editor.swagger.io/. To give a simple example: How to write in the following YAML, that /login action creates a Cookie and the Cookie has to be passed to /showMySecretStuff?
swagger: '2.0'
info:
title: Test API
version: '1'
host: my.test.com
schemes:
- https
basePath: /
consumes:
- multipart/form-data
produces:
- application/json
paths:
/login:
post:
parameters:
- name: username
in: formData
required: true
type: string
- name: password
in: formData
required: true
type: string
default: secret
responses:
200:
description: OK
/showMySecretStuff:
get:
responses:
200:
description: OK
Cookie authentication is supported in OpenAPI 3.0 but not in OpenAPI/Swagger 2.0.
In OpenAPI 3.0, cookie authentication is defined as an API key that is sent in: cookie:
openapi: 3.0.1
...
components:
securitySchemes:
cookieAuth:
type: apiKey
in: cookie
name: COOKIE-NAME # replace with your cookie name
paths:
/showMySecretStuff:
get:
security:
- cookieAuth: []
responses:
'200':
description: OK
The login operation is not linked to securitySchemes in any way, but you may want to define the response header Set-Cookie for documentation purposes:
paths:
/login:
post:
requestBody:
...
responses:
'200':
description: OK
headers:
Set-Cookie:
description: >
Contains the session cookie named `COOKIE-NAME`.
Pass this cookie back in subsequent requests.
schema:
type: string
That said, Swagger Editor and Swagger UI currently don't support cookie authentication. Check out the OAS 3.0 Support Backlog and this issue for updates.
Cookie auth is supported in SwaggerHub though. (Disclosure: SwaggerHub is a product of the company I work for.)