Issue with Tyk Identity Broker ProxyProvider configuration to issue api keys

I’m using Tyk Identity Broker with a ProxyProvider and everything is working except when the valid response comes back from my service, the call to the tyk dashboard API fails saying that my api user doesn’t have the right permissions:

WARN[0003] Response code was: 403                       
WARN[0003] GOT:{"Status":"Error","Message":"User does not have permission to add API to key Access Rights!","Meta":null}
ERRO[0003] [TYK ID HANDLER] --> Login failure. Request not allowed 

I’ve ensured that my profiles.json has the correct data:

[{
“ActionType”: “GenerateTemporaryAuthToken”,
“ID”: “1”,
“IdentityHandlerConfig”: {
“DashboardCredential”: “cf093e9bef1a463a754c05d6b389ec1f”
},
“OrgID”: “570e58d9e11a690001000001”,
“ProviderConfig”: {
“AccessTokenField”: “access_token”,
“ExtractUserNameFromBasicAuthHeader”: true,
“OKCode”: 200,
“OKRegex”: “”,
“OKResponse”: “”,
“ResponseIsJson”: true,
“TargetHost”: “http://192.168.99.100:4000/login/”,
“UsernameField”: “user_name”
},
“ProviderName”: “ProxyProvider”,
“ReturnURL”: “http://192.168.99.100:3000”,
“Type”: “redirect”
}]

and that my user has both apis:read and keys:write permissions:

# curl -s -X GET -H 'admin-auth: 12345' http://192.168.99.100:3000/admin/users/570e5a3f2e87148c2b4dde13 | python -mjson.tool`
 {
     "access_key": "cf093e9bef1a463a754c05d6b389ec1f",
     "active": true,
     "api_model": {},
     "email_address": "[email protected]",
     "first_name": "API",
     "id": "570e5a3f2e87148c2b4dde13",
     "last_name": "KEYISSUER",
     "org_id": "570e58d9e11a690001000001",
     "password": "$2a$10$.udWb1HPIPGHte.ji.BLzu8TO5JxN8SLRDTf3kXY6HOlQZ2.ZCrwK",
     "user_permissions": {
         "apis": "read",
        "keys": "write"
      }
 }

What other permissions do I need for this api user?

Most likely, the API you created is not pegged to an Org, make sure the API that you have stored in Tyk Dashboard has the same ORG ID as the user.

My API has the same org id as the user:

api object form mongo:

{ “_id” : ObjectId(“570e6abde11a690001000002”), “name” : “Login API”, “slug” : “login-api”, “api_id” : “3a642dd280ae449e4f10f50606f1ade1”, “org_id” : “570e58d9e11a690001000001”, “use_keyless” : false, “use_oauth2” : false, “oauth_meta” : { “allowed_access_types” : [ ], “allowed_authorize_types” : [ ], “auth_login_redirect” : “” }, “auth” : { “use_param” : false, “use_cookie” : false, “auth_header_name” : “Authorization” }, “use_basic_auth” : false, “enable_jwt” : false, “jwt_signing_method” : “”, “jwt_source” : “”, “jwt_identity_base_field” : “”, “jwt_policy_field_name” : “”, “notifications” : { “shared_secret” : “”, “oauth_on_keychange_url” : “” }, “enable_signature_checking” : false, “hmac_allowed_clock_skew” : -1, “definition” : { “location” : “header”, “key” : “x-api-version” }, “version_data” : { “not_versioned” : true, “versions” : { “RGVmYXVsdA==” : { “name” : “RGVmYXVsdA==”, “expires” : “”, “paths” : { “ignored” : [ ], “white_list” : [ ], “black_list” : [ ] }, “use_extended_paths” : true, “extended_paths” : { “ignored” : [ ], “white_list” : [ ], “black_list” : [ ], “cache” : [ ], “transform” : [ ], “transform_response” : [ ], “transform_headers” : [ ], “transform_response_headers” : [ ], “hard_timeouts” : [ ], “circuit_breakers” : [ ], “url_rewrites” : [ ], “virtual” : [ ], “size_limits” : [ ] }, “global_headers” : { }, “global_headers_remove” : [ ], “global_size_limit” : NumberLong(0), “override_target” : “” } } }, “uptime_tests” : { “check_list” : [ ], “config” : { “expire_utime_after” : NumberLong(0), “service_discovery” : { “use_discovery_service” : false, “query_endpoint” : “”, “use_nested_query” : false, “parent_data_path” : “”, “data_path” : “”, “port_data_path” : “”, “target_path” : “”, “use_target_list” : false, “cache_timeout” : NumberLong(60), “endpoint_returns_list” : false }, “recheck_wait” : 0 } }, “proxy” : { “preserve_host_header” : false, “listen_path” : “/login-api/”, “target_url” : “http://192.168.99.100:4000/”, “strip_listen_path” : true, “enable_load_balancing” : false, “target_list” : [ ], “check_host_against_uptime_tests” : false, “service_discovery” : { “use_discovery_service” : false, “query_endpoint” : “”, “use_nested_query” : false, “parent_data_path” : “”, “data_path” : “hostname”, “port_data_path” : “port”, “target_path” : “/api-slug”, “use_target_list” : false, “cache_timeout” : NumberLong(60), “endpoint_returns_list” : false } }, “custom_middleware” : { “pre” : [ ], “post” : [ ], “response” : [ ] }, “cache_options” : { “cache_timeout” : NumberLong(60), “enable_cache” : true, “cache_all_safe_requests” : false, “enable_upstream_cache_control” : false }, “session_lifetime” : NumberLong(0), “active” : true, “auth_provider” : { “name” : “”, “storage_engine” : “”, “meta” : { } }, “session_provider” : { “name” : “”, “storage_engine” : “”, “meta” : null }, “event_handlers” : { “events” : { } }, “enable_batch_request_support” : false, “enable_ip_whitelisting” : false, “allowed_ips” : [ ], “dont_set_quota_on_create” : false, “expire_analytics_after” : NumberLong(0), “response_processors” : [ ], “CORS” : { “enable” : false, “allowed_origins” : [ ], “allowed_methods” : [ ], “allowed_headers” : [ ], “exposed_headers” : [ ], “allow_credentials” : false, “max_age” : 24, “options_passthrough” : false, “debug” : false }, “domain” : “”, “do_not_track” : false, “tags” : [ ], “hook_references” : [ ], “is_site” : false, “sort_by” : 0 }

user from API:

curl -s -X GET -H ‘admin-auth: 12345’ http://192.168.99.100:3000/admin/users/570e5a3f2e87148c2b4dde13 | python -mjson.tool

{
“access_key”: “cf093e9bef1a463a754c05d6b389ec1f”,
“active”: true,
“api_model”: {},
“email_address”: "[email protected]",
“first_name”: “API”,
“id”: “570e5a3f2e87148c2b4dde13”,
“last_name”: “KEYISSUER”,
“org_id”: “570e58d9e11a690001000001”,
“password”: “$2a$10$.udWb1HPIPGHte.ji.BLzu8TO5JxN8SLRDTf3kXY6HOlQZ2.ZCrwK”,
“user_permissions”: {
“apis”: “read”,
“keys”: “write”
}
}

What’s not clear to me is how TIB knows what API I’m talking about.

profiles.json doesn’t have any information about a specific api id:

cat profiles.json

[{
“ActionType”: “GenerateTemporaryAuthToken”,
“ID”: “1”,
“IdentityHandlerConfig”: {
“DashboardCredential”: “cf093e9bef1a463a754c05d6b389ec1f”
},
“OrgID”: “570e58d9e11a690001000001”,
“ProviderConfig”: {
“AccessTokenField”: “access_token”,
“ExtractUserNameFromBasicAuthHeader”: true,
“OKCode”: 200,
“OKRegex”: “”,
“OKResponse”: “”,
“ResponseIsJson”: true,
“TargetHost”: “http://192.168.99.100:4000/login/”,
“UsernameField”: “user_name”
},
“ProviderName”: “ProxyProvider”,
“ReturnURL”: “http://192.168.99.100:3000”,
“Type”: “redirect”
}]

How do I tell TIB which API I’m issuing keys for?

Ah, ok, it might be your policy then. Tyk does have information about which API, it gets a policy ID, the policy ID has access rights set for each API being granted access to, if the policy, or any APIs listed in the policy file are not tagged with the same org ID, there’s a problem.

You are also missing some stuff, check out the example docs, you need the base API ID in your profile:

"ActionType": "GenerateTemporaryAuthToken",
"ID": "8",
"IdentityHandlerConfig": {
    "DashboardCredential": "822f2b1c75dc4a4a522944caa757976a",
    "DisableOneTokenPerAPI": false,
    "TokenAuth": {
        "BaseAPIID": "e1d21f942ec746ed416ab97fe1bf07e8"
    }
},
"MatchedPolicyID": "5654566b30c55e3904000003",

In fact, quite a bit is missing from that profile… recommend using the example I’ve linked as a baseline.

(to add code blocks, either indent by 4 characters, or use three backticks to open and close a block)

(Before you ask: You need a Base API ID so that the token has a fallback if the policy gets deleted.)

Ah, thanks, I’ll get that updated with those details

Almost there. Thanks for the help. Any insight on this error message? I’m not sure what’s causing the issue:

$ curl -i -X POST -H 'Authorization: 3420cda7598847a86a910022ab976f58' http://192.168.99.100:3000/api/keys -d '{"last_check": 0,"allowance": 1000,"rate": 1000,"per": 60,"expires": 0,"quota_max": 10000,"quota_renews": 1424543479,"quota_remaining": 10000,"quota_renewal_rate": 2520000,"access_rights": {"3a642dd280ae449e4f10f50606f1ade1": {"api_id": "3a642dd280ae449e4f10f50606f1ade1","api_name": "Login API","versions": ["Default"]}}}'
HTTP/1.1 403 Forbidden
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Wed, 13 Apr 2016 19:48:56 GMT
Content-Length: 157

{"Status":"Error","Message":"Failed to save new session object to Tyk: Failed to create key: Post http://tyk_gateway:8080/tyk/keys/create: EOF","Meta":null}

from the gateway logs:

time="Apr 13 19:48:56" level=info msg="Reset quota for key." inbound-key=570e58d9e11a690001000001789eaed5f72d49ed7e21e3bda92d3a4b key=quota-c03e92f2 
2016/04/13 19:48:56 http: panic serving 172.18.0.4:49845: runtime error: invalid memory address or nil pointer dereference
goroutine 174 [running]:
net/http.(*conn).serve.func1(0xc82057c480)
	/usr/local/go/src/net/http/server.go:1389 +0xc1
panic(0xc55920, 0xc820012070)
	/usr/local/go/src/runtime/panic.go:426 +0x4e9
main.createKeyHandler(0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/home/tyk/go/src/github.com/lonelycode/tyk/api.go:1316 +0x141b
main.CheckIsAPIOwner.func1(0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/home/tyk/go/src/github.com/lonelycode/tyk/middleware_api_security_handler.go:24 +0xe1
net/http.HandlerFunc.ServeHTTP(0xc8204ee580, 0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/usr/local/go/src/net/http/server.go:1618 +0x3a
github.com/gorilla/mux.(*Router).ServeHTTP(0xc82045e190, 0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/home/tyk/go/src/github.com/gorilla/mux/mux.go:98 +0x29e
net/http.(*ServeMux).ServeHTTP(0xc820555d40, 0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/usr/local/go/src/net/http/server.go:1910 +0x17d
net/http.serverHandler.ServeHTTP(0xc8203a2c00, 0x7f7779a80fa0, 0xc8202b8270, 0xc8200729a0)
	/usr/local/go/src/net/http/server.go:2081 +0x19e
net/http.(*conn).serve(0xc82057c480)
	/usr/local/go/src/net/http/server.go:1472 +0xf2e
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2137 +0x44e

Ok, so this is an edge case, which I’ll try to address in code later, but basically, your access rights contains an Open API (non auth), and you can only add keys to APIs that have Auth enabled. So basically, replace the Access Rights section with a closed API, even if it’s a dummy.

Also, don’t inclue Open APIs in access lists.

Hmmm…something weird is definitely going on. I have the API set to use ‘Auth Token’ auth. Was going to paste the API response to show that but something about that API entry is causing the application to crash:

$ curl -X GET -H 'Authorization: 3420cda7598847a86a910022ab976f58' http://192.168.99.100:3000/api/apis/bc2f8cfb7ab241504d9f3574fe407499
Application Error

Here’s the log from the dashboard:

ERROR 2016/04/13 20:15:10.590827 panic_handler.go:26: PANIC
URL: /api/apis/bc2f8cfb7ab241504d9f3574fe407499
ERROR: Invalid input to ObjectIdHex: "bc2f8cfb7ab241504d9f3574fe407499"
STACK:
goroutine 12462 [running]:
github.com/gocraft/web.(*Router).handlePanic(0xc8202a2420, 0xc82037d800, 0xc82037d820, 0xa60f80, 0xc82032ae70)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:251 +0x34e
github.com/gocraft/web.(*Router).ServeHTTP.func1(0xc8202a2420, 0xc82037d800)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:43 +0x68
panic(0xa60f80, 0xc82032ae70)
	/usr/local/go/src/runtime/panic.go:426 +0x4e9
gopkg.in/mgo.v2/bson.ObjectIdHex(0xc82027dc4e, 0x20, 0x0, 0x0)
	/home/tyk/go/src/gopkg.in/mgo.v2/bson/bson.go:175 +0x210
main.(*ApiDefinition).GetApiByInternalId(0xc820272a00, 0xc8202b0a60, 0x18, 0xc82027dc4e, 0x20, 0x0)
	/home/tyk/go/src/github.com/lonelycode/tyk-analytics/ModelApiDefintion.go:228 +0x1a1
main.(*APIContext).GetApiDetail(0xc820021430, 0x7f36cedb1a88, 0xc82037d800, 0xc82037d820)
	/home/tyk/go/src/github.com/lonelycode/tyk-analytics/ApiApiManagement.go:152 +0x111
reflect.Value.call(0xaf26a0, 0xde5f28, 0x13, 0xc70d58, 0x4, 0xc820408ac8, 0x3, 0x3, 0x0, 0x0, ...)
	/usr/local/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(0xaf26a0, 0xde5f28, 0x13, 0xc820408ac8, 0x3, 0x3, 0x0, 0x0, 0x0)
	/usr/local/go/src/reflect/value.go:303 +0xb1
github.com/gocraft/web.middlewareStack.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:113 +0xad4
main.(*APIContext).ApiAuthorisationRequired(0xc820021430, 0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/lonelycode/tyk-analytics/Main.go:425 +0x37b
reflect.Value.call(0xb2e1a0, 0xde5e38, 0x13, 0xc70d58, 0x4, 0xc820409060, 0x4, 0x4, 0x0, 0x0, ...)
	/usr/local/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(0xb2e1a0, 0xde5e38, 0x13, 0xc820409060, 0x4, 0x4, 0x0, 0x0, 0x0)
	/usr/local/go/src/reflect/value.go:303 +0xb1
github.com/gocraft/web.(*middlewareHandler).invoke(0xc8202f2d20, 0xc6d840, 0xc820021430, 0x16, 0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:133 +0x286
github.com/gocraft/web.middlewareStack.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:122 +0x1a5
github.com/gocraft/web.StaticMiddlewareFromDir.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/gocraft/web/static_middleware.go:30 +0x16e
github.com/gocraft/web.(*middlewareHandler).invoke(0xc8202f24b0, 0xc2b200, 0xc8200213d0, 0x16, 0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:131 +0x6e
github.com/gocraft/web.middlewareStack.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:122 +0x1a5
github.com/gocraft/web.StaticMiddlewareFromDir.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/gocraft/web/static_middleware.go:30 +0x16e
github.com/gocraft/web.(*middlewareHandler).invoke(0xc8202f2480, 0xc2b200, 0xc8200213d0, 0x16, 0x7f36cedb1a88, 0xc82037d800, 0xc82037d820, 0xc82032a610)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:131 +0x6e
github.com/gocraft/web.middlewareStack.func1(0x7f36cedb1a88, 0xc82037d800, 0xc82037d820)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:122 +0x1a5
github.com/gocraft/web.(*Router).ServeHTTP(0xc8202a2420, 0x7f36cedb1a28, 0xc8202781a0, 0xc8203707e0)
	/home/tyk/go/src/github.com/gocraft/web/router_serve.go:48 +0x355
net/http.(*ServeMux).ServeHTTP(0xc8202f2120, 0x7f36cedb1a28, 0xc8202781a0, 0xc8203707e0)
	/usr/local/go/src/net/http/server.go:1910 +0x17d
net/http.serverHandler.ServeHTTP(0xc8202eee80, 0x7f36cedb1a28, 0xc8202781a0, 0xc8203707e0)
	/usr/local/go/src/net/http/server.go:2081 +0x19e
net/http.(*conn).serve(0xc8202ef300)
	/usr/local/go/src/net/http/server.go:1472 +0xf2e
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2137 +0x44e

Maybe this is because I have multiple Organizations with the same api name? I’ll try adding a totally new dummy API and see if that works

That error is because the API ID you are using isn’t the right one.

APIs in the dash have two representations, the ObjectID (DB PK) and the API ID - the API ID is the portable bit, and is what is used everywhere, this is the one that shows up beside the API name in the API list.

The internal object ID is different, and is used by the Dashboard API to view the API (this is the id field or the bit in the browser when you browse to the API).

There’s something odder at work here, in your CURL request, your API ID is: 3a642dd280ae449e4f10f50606f1ade1 but in the API request to the dashboard it is bc2f8cfb7ab241504d9f3574fe407499 - or have you changed them since?

Ah, I was grabbing off the API table in the dashboard, the call works fine when I grab the api id from the URI.

$ curl -s -X GET -H 'Authorization: 3420cda7598847a86a910022ab976f58' http://192.168.99.100:3000/api/apis/570ea953e11a690001000004 | python -mjson.tool
{
    "api_definition": {
        "CORS": {
            "allow_credentials": false,
            "allowed_headers": [],
            "allowed_methods": [],
            "allowed_origins": [],
            "debug": false,
            "enable": false,
            "exposed_headers": [],
            "max_age": 24,
            "options_passthrough": false
        },
        "active": true,
        "allowed_ips": [],
        "api_id": "44d0623fe37d447d749d859ef60d9c39",
        "auth": {
            "auth_header_name": "Authorization",
            "use_cookie": false,
            "use_param": false
        },
        "auth_provider": {
            "meta": {},
            "name": "",
            "storage_engine": ""
        },
        "cache_options": {
            "cache_all_safe_requests": false,
            "cache_timeout": 60,
            "enable_cache": true,
            "enable_upstream_cache_control": false
        },
        "custom_middleware": {
            "post": [],
            "pre": [],
            "response": []
        },
        "definition": {
            "key": "x-api-version",
            "location": "header"
        },
        "do_not_track": false,
        "domain": "",
        "dont_set_quota_on_create": false,
        "enable_batch_request_support": false,
        "enable_ip_whitelisting": false,
        "enable_jwt": false,
        "enable_signature_checking": false,
        "event_handlers": {
            "events": {}
        },
        "expire_analytics_after": 0,
        "hmac_allowed_clock_skew": -1,
        "id": "570ea953e11a690001000004",
        "jwt_identity_base_field": "",
        "jwt_policy_field_name": "",
        "jwt_signing_method": "",
        "jwt_source": "",
        "name": "Dummy API",
        "notifications": {
            "oauth_on_keychange_url": "",
            "shared_secret": ""
        },
        "oauth_meta": {
            "allowed_access_types": [],
            "allowed_authorize_types": [],
            "auth_login_redirect": ""
        },
        "org_id": "570e58d9e11a690001000001",
        "proxy": {
            "check_host_against_uptime_tests": false,
            "enable_load_balancing": false,
            "listen_path": "/dummy-api/",
            "preserve_host_header": false,
            "service_discovery": {
                "cache_timeout": 60,
                "data_path": "hostname",
                "endpoint_returns_list": false,
                "parent_data_path": "",
                "port_data_path": "port",
                "query_endpoint": "",
                "target_path": "/api-slug",
                "use_discovery_service": false,
                "use_nested_query": false,
                "use_target_list": false
            },
            "strip_listen_path": true,
            "target_list": [],
            "target_url": "http://192.168.99.100:4000/"
        },
        "response_processors": [],
        "session_lifetime": 0,
        "session_provider": {
            "meta": null,
            "name": "",
            "storage_engine": ""
        },
        "slug": "dummy-api",
        "tags": [],
        "uptime_tests": {
            "check_list": [],
            "config": {
                "expire_utime_after": 0,
                "recheck_wait": 0,
                "service_discovery": {
                    "cache_timeout": 60,
                    "data_path": "",
                    "endpoint_returns_list": false,
                    "parent_data_path": "",
                    "port_data_path": "",
                    "query_endpoint": "",
                    "target_path": "",
                    "use_discovery_service": false,
                    "use_nested_query": false,
                    "use_target_list": false
                }
            }
        },
        "use_basic_auth": false,
        "use_keyless": false,
        "use_oauth2": false,
        "version_data": {
            "not_versioned": true,
            "versions": {
                "Default": {
                    "expires": "",
                    "extended_paths": {
                        "black_list": [],
                        "cache": [],
                        "circuit_breakers": [],
                        "hard_timeouts": [],
                        "ignored": [],
                        "size_limits": [],
                        "transform": [],
                        "transform_headers": [],
                        "transform_response": [],
                        "transform_response_headers": [],
                        "url_rewrites": [],
                        "virtual": [],
                        "white_list": []
                    },
                    "global_headers": {},
                    "global_headers_remove": [],
                    "global_size_limit": 0,
                    "name": "Default",
                    "override_target": "",
                    "paths": {
                        "black_list": [],
                        "ignored": [],
                        "white_list": []
                    },
                    "use_extended_paths": true
                }
            }
        }
    },
    "api_model": {},
    "hook_references": [],
    "is_site": false,
    "sort_by": 0
}

Ok that is odd… the only time I have seen something similar is in the middle of a hot reload. If you try to create the key again what happens?

Can you set a content-type of application/json in your curl request actually, the Tyk gateway API is very picky about that

Sure. Looks like I’m getting closer. This API is one created by the same user so I’m not sure why I don’t have access rights:

$ curl -s -X GET -H 'admin-auth: 12345' http://192.168.99.100:3000/admin/users/570e5a3f2e87148c2b4dde13 | python -mjson.tool
{
    "access_key": "f5992371eaa543f754e5b4da718ba679",
    "active": true,
    "api_model": {},
    "email_address": "[email protected]",
    "first_name": "API",
    "id": "570e5a3f2e87148c2b4dde13",
    "last_name": "KEYISSUER",
    "org_id": "570e58d9e11a690001000001",
    "password": "$2a$10$.udWb1HPIPGHte.ji.BLzu8TO5JxN8SLRDTf3kXY6HOlQZ2.ZCrwK",
    "user_permissions": {
        "apis": "read",
        "keys": "write"
    }
}

$ curl -s -X POST -H 'Content-type: application/json' -H 'Authorization: f5992371eaa543f754e5b4da718ba679' http://192.168.99.100:3000/api/keys -d '{"last_check": 0,"allowance": 1000,"rate": 1000,"per": 60,"expires": 0,"quota_max": 10000,"quota_renews": 1424543479,"quota_remaining": 10000,"quota_renewal_rate": 2520000,"access_rights": {"570ea953e11a690001000004": {"api_id": "570ea953e11a690001000004","api_name": "Dummy API","versions": ["Default"]}}}' | python -mjson.tool
{
    "Message": "User does not have permission to add API to key Access Rights!",
    "Meta": null,
    "Status": "Error"
}

Actually, I see the issue, I was using the primary key again, not the api_id as you need to get the api details:

curl -s -X GET -H 'Authorization: f5992371eaa543f754e5b4da718ba679' http://192.168.99.100:3000/api/apis/570ea953e11a690001000004 | python -mjson.tool
{
    "api_definition": {
        "CORS": {
            "allow_credentials": false,
            "allowed_headers": [],
            "allowed_methods": [],
            "allowed_origins": [],
            "debug": false,
            "enable": false,
            "exposed_headers": [],
            "max_age": 24,
            "options_passthrough": false
        },
        "active": true,
        "allowed_ips": [],
        "api_id": "44d0623fe37d447d749d859ef60d9c39",

What “column” on the policy is what I should use for “MatchedPolicyID” in profiles.json? I’ve been using the primary key but that doesn’t seem to work:

{ "_id" : ObjectId("570e748ce11a690001000003"), "org_id" : "570e58d9e11a690001000001", "rate" : 1000, "per" : 60, "quota_max" : NumberLong(-1), "quota_renewal_rate" : NumberLong(60), "access_rights" : { "44d0623fe37d447d749d859ef60d9c39" : { "apiname" : "Dummy API", "apiid" : "44d0623fe37d447d749d859ef60d9c39", "versions" : [ "Default" ], "allowed_urls" : [ ] }, "3a642dd280ae449e4f10f50606f1ade1" : { "apiname" : "Login API", "apiid" : "3a642dd280ae449e4f10f50606f1ade1", "versions" : [ "Default" ], "allowed_urls" : [ ] } }, "hmac_enabled" : false, "active" : true, "name" : "Dummy Policy", "is_inactive" : false, "date_created" : ISODate("0001-01-01T00:00:00Z"), "tags" : [ ], "key_expires_in" : NumberLong(0) }

I’ve traced this key error, and it’s because there is no API Spec in place, and stupidly, the method that does the update with the fallback storage handler requires the nil object, which is what causes the panic.

Can you restart your tyk-gateway service and share the logs from the point of the restart? I think somethings gone very wrong with how it’s loading up your API definitions.

Yea, looks like there’s some invalid json somewhere:

$ docker logs bf6c954c647c
time="Apr 13 20:46:34" level=info msg="Connection dropped, connecting.." 
time="Apr 13 20:46:34" level=warning msg="Cache purging is no longer part of Tyk Gateway, please use Tyk-Pump." 
time="Apr 13 20:46:34" level=info msg="Setting up Server" 
time="Apr 13 20:46:34" level=info msg="--> Standard listener (http)" 
time="Apr 13 20:46:34" level=info msg="Registering node." 
time="Apr 13 20:46:34" level=error msg="Failed to register node, retrying in 5s" 
time="Apr 13 20:46:39" level=error msg="Failed to register node, retrying in 5s" 
time="Apr 13 20:46:44" level=info msg="Starting Poller" 
time="Apr 13 20:46:44" level=error msg="Failed to register node, retrying in 5s" 
time="Apr 13 20:46:49" level=info msg="Node registered" id=5e44ffb5-3580-46c7-71e8-9b348fa7e486 
time="Apr 13 20:46:49" level=info msg="Starting heartbeat." 
time="Apr 13 20:46:49" level=info msg="Detected 10 APIs" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal Assets" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: www.tyk-portal-test.com" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Open" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal Assets" 
time="Apr 13 20:46:49" level=error msg="Duplicate listen path found, skipping. API ID: 1f7701e7f1c640017b8fd0d66fbac8a7" 
time="Apr 13 20:46:49" level=warning msg="----> Skipped!" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal API" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: www.tyk-portal-test.com" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Open" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal API" 
time="Apr 13 20:46:49" level=error msg="Duplicate listen path found, skipping. API ID: 04401112703f49af4d9bd4c2172e8eb9" 
time="Apr 13 20:46:49" level=warning msg="----> Skipped!" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: www.tyk-portal-test.com" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Open" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Portal" 
time="Apr 13 20:46:49" level=error msg="Duplicate listen path found, skipping. API ID: feda9795301d4f4e5f6992ae3a638dcd" 
time="Apr 13 20:46:49" level=warning msg="----> Skipped!" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Organization Member Service" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: (no host)" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Basic" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Login API" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: (no host)" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Basic" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Login API" 
time="Apr 13 20:46:49" level=error msg="Duplicate listen path found, skipping. API ID: 3a642dd280ae449e4f10f50606f1ade1" 
time="Apr 13 20:46:49" level=warning msg="----> Skipped!" 
time="Apr 13 20:46:49" level=info msg="--> Loading API: Dummy API" 
time="Apr 13 20:46:49" level=info msg="----> Tracking: (no host)" 
time="Apr 13 20:46:49" level=info msg="----> Checking security policy: Token" 
time="Apr 13 20:46:49" level=info msg="Loading uptime tests..." 
time="Apr 13 20:46:49" level=error msg="Failed to decode body: json: cannot unmarshal string into Go value of type []main.Policy" 
time="Apr 13 20:46:49" level=info msg="Gateway started (v2.0.0.0)" 
time="Apr 13 20:46:49" level=info msg="--> Listening on port: 8080"

That JSON error isn’t worrying me yet, it’s a first load with no policies (odd, but ok - your policies may not be loading for another reason, that will fix itself as soon as you re-save the policy.

The issue is that you have duplicate listen paths for a bunch of your APIs, so Tyk Gateway isn;t loading them, which is causing the failure when Tyk is trying to save a key.

Notice that there are two login APIs, you need to delete one of those (or change it’s listen path) - it looks like the one using basic auth is being loaded and the other (using standard bearer Auth I believe) is being skipped, and that’s the one you want running to create your key.

I’d also suggest making sure that all LIVE APIs have a unique listen path set, otherwise they will skip like this because the routing table doesn’t allow collisions.

Btw the policy ID is indeed the PK (the bit that is in the browser URL when you view the policy).

Thanks, removing the duplicate apis now.

Looks like the policies weren’t being loaded as the tyk.conf had some placeholders in there that I didn’t notice.