[
  {
    "name": "HTTPAuth",
    "comments": null,
    "auth_type_ident": 3,
    "class_name": "com.kahuna.server.auth.HttpAuthProvider",
    "javascript_authprovider_code": null,
    "bootstrap_config_value": null,
    "param_map": null,
    "get_config_info_code": "var pkField = configInfo.addConfigField();\npkField.name = \"AuthPublicKeyText\";\npkField.description = \"The public key to verify incoming authentication\";\npkField.display = \"Auth public key\";\npkField.length = 500;\n\nvar pkAlgoField = configInfo.addConfigField();\npkAlgoField.name = \"AuthPublicKeyAlgorithm\";\npkAlgoField.description = \"The algorithm for the public key\";\npkAlgoField.display = \"Auth public key algorithm\";\npkAlgoField.length = 20;\n\nvar jwtHeaderName = configInfo.addConfigField();\njwtHeaderName.name = \"AuthHeaderName\";\njwtHeaderName.description = \"Name of HTTP header containing authentication, used for caching (see docs for details)\";\njwtHeaderName.display = \"Auth Header Name\";\njwtHeaderName.length = 20;\n",
    "configure_code": null,
    "get_login_info_code": "var auth = servletRequest.getHeader(\"Authorization\");\nif (auth && auth.startsWith(\"Bearer \")) {\n    // In this very simple example, we just assume that the header is OK\n\t\t// Obviously, in the real world, you would want to verify that!\n    loginInfo.loginStatus = \"OK\";\n\n\t\t// The authentication information should provide you with enough\n\t\t// information to determine the caller's account. Here we just\n\t\t// hard-code it.\n    loginInfo.accountIdent = 1000;\n    return;\n}\n// If the request did not contain enough information, let's send back\n// a URL to tell the caller where to go for authentication\nif ( ! authProvider.variables[\"randomState\"]) {\n    print(\"Get login info: generate random state\");\n    var RG = Java.type(\"com.kahuna.logic.lib.crypto.RandomGenerator\");\n    authProvider.variables.randomState = RG.generateRandomString(50);\n}\nvar clientId = \"12345678901234567890\";\nvar redirectUri = \"http://localhost:8080/APICreator\";\nloginInfo.redirectUrl =\n    \"https://dev-1234567.oktapreview.com/oauth2/default/v1/authorize?client_id=\" +\n    clientId + \"&response_type=token&scope=openid&redirect_uri=\" + redirectUri +\n    \"&state=\" + authProvider.variables.randomState +\n    \"&nonce=\" + authProvider.variables.randomState;\n\t\n",
    "auth_code": null,
    "account_ident": null
  },
  {
    "name": "JSAUth",
    "comments": "test",
    "auth_type_ident": 2,
    "class_name": "com.kahuna.server.auth.JavaScriptAuthProvider",
    "javascript_authprovider_code": "out = java.lang.System.out;\n\nfunction create() {\n\n    var result = {};\n    var configSetup = {\n        logonApiKey : '',\n        loginBaseURL : '',\n        loginGroupURL : '',\n        keyLifetimeMinutes : 60\n    };\n\n\n    // This function is called by API Creator when the user enters a value\n    // for the parameters specified by getConfigInfo and clicks Save.\n    // Returns configuration values which API Creator will save into Admin DB;\n    // these are exported in the json config file\n    result.configure = function configure(myConfig) {\n        configSetup.logonApiKey = myConfig.logonApiKey || 'demo_full';  // supply, or default\n        configSetup.loginBaseURL = myConfig.loginBaseURL || 'http://localhost:8080/rest/default/v1/...';\n        configSetup.keyLifetimeMinutes = myConfig.keyLifetimeMinutes || 60;\n    };\n\n\n    // Main crux of Auth Provider - called by API Server on post to @authenticate, to return list of Roles\n    // NOTE: the function configure must be called first - this will validate the user/pw\n\n    // The argument passed in will contain whatever values were provided to the @authentication service.\n    // If the caller is well-behaved, this should correspond to the parameters described by getLoginInfo,\n    // but you should not depend on that.\n\n    // This function must return an object containing just an error message if the authentication failed.\n    // If the authentication succeeded, then it must return an object with the following properties:\n    result.authenticate = function authenticate(payload) {\n\n        out.println(\"Authentication called...\");\n\n        var roles = [];\n        var errorMsg = null;\n        var myUserData = [];\n        var params = null;\n\n        try {\n            if (payload.username == 'admin' || payload.username == 'demo') {\n                out.println(\"Authentication - default admin/demo/ user - good to go..\");\n                roles.push('Full access'); // HARD CODED FOR DEMO -Some examples use this as their role name\n                roles.push('API Owner'); // HARD CODED FOR DEMO (we even ignore the pwd) for full access\n                errorMsg = null; // authorized successfully\n                myUserData.push(\"Free Form Data\");\n            }\n        }\n        catch (e) {\n            errorMsg = e.message;\n        }\n\n        var autResponse = {\n            errorMessage : errorMsg,\n            roleNames : roles,\n            userIdentifier : payload.username,\n            keyExpiration : new Date(+new Date()\n                    + (+configSetup.keyLifetimeMinutes) * 60 * 1000),\n            userData : myUserData,\n            lastLogin : {\n                datetime : null,\n                ipAddress : null\n            }\n        };\n        return autResponse;\n    };\n\n    // FUNCTION getLoginInfo is used to create the login dialog - DO NOT CHANGE\n    // This function is called by API Server when a client needs to know what kind of information is required for authentication.\n    // Basically, this describes what the login dialog should look like (assuming the client is an interactive application).\n    result.getLoginInfo = function getLoginInfo() {\n        return {\n            fields : [\n                    {\n                        name : \"username\",\n                        display : \"Username\",\n                        description : \"Enter your First Name\",\n                        type : \"text\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com\"\n                    },\n                    {\n                        name : \"password\",\n                        display : \"Password\",\n                        description : \"Enter your Last Name as Password\",\n                        type : \"password\",\n                        length : 40,\n                        helpURL : \"http://liveapicreator.ca.com/\"\n                    } ],\n            links : [\n\n            ]\n        };\n    };\n\n   //getConfigInfo is used to generate the Auth Provider User Interface to prompt for variables passed to the system at authentication time\n    result.getConfigInfo = function getConfigInfo() {\n        return {\n            current : {\n                \"keyLifetimeMinutes\" : configSetup.keyLifetimeMinutes,\n                \"logonApiKey\" :        configSetup.logonApiKey,\n                \"loginBaseURL\" :       configSetup.loginBaseURL\n            },\n            fields : [ {\n                name : \"logonApiKey\",\n                display : \"logonApiKey\",\n                type : \"text\",\n                length : 60,\n                helpURL : \"\"\n            }, {\n                name : \"loginBaseURL\",\n                display : \"loginBaseURL\",\n                type : \"text\",\n                length : 120,\n                helpURL : \"\"\n            }, {\n                name : \"loginGroupURL\",\n                display : \"loginGroupURL\",\n                type : \"text\",\n                length : 120,\n                helpURL : \"\"\n            }, {\n                name : \"keyLifetimeMinutes\",\n                display : \"API Key Lifetime (Minutes)\",\n                type : \"number\",\n                length : 8,\n                helpURL : \"http://www.liveapicreator.ca.com\"\n            } ],\n            links : []\n        };\n    };\n\n    // returns object containing the 4 functions that define a Custom Authentication Provider:\n    //   getConfigInfo: function() {...},  configure: function(values) {...}, getLoginInfo: function() {...}, authenticate:\n\n    return result;\n}\n",
    "bootstrap_config_value": "create",
    "param_map": "\"keyLifetimeMinutes\"=\"60\", \"loginBaseURL\"=\"http://localhost:8080/rest/default/b2bderbynw/v1/nw:Employees\", \"loginGroupURL\"=\"http://localhost:8080/rest/default/demo/v1/demo:employee\", \"logonApiKey\"=\"Bzn8jVyfOTiIpW6UQCgy\"",
    "get_config_info_code": null,
    "configure_code": null,
    "get_login_info_code": null,
    "auth_code": null,
    "account_ident": null
  },
  {
    "name": "SimpleLDAP",
    "comments": null,
    "auth_type_ident": 2,
    "class_name": "com.kahuna.server.auth.JavaScriptAuthProvider",
    "javascript_authprovider_code": "// This is a simple LDAP authentication provider for CA Live API Creator.\n// It connects to a public LDAP server (ldap.forumsys.com), which recognizes\n// a few users: boyle, curie, einstein, euclid, euler, gauss, newton, pasteur, tesla.\n// All users have the same password: \"password\" (without quotes).\n// Each user belongs to a group: chemists, mathematicians, or scientists.\n// Each user has an attribute named mail, and some users have an attribute named telephoneNumber.\n//\n// To adapt this to your specific LDAP environment, you will have to change the code to fit\n// your schema, e.g. what attribures are relevant, how you figure out which roles a user has, etc...\n//\n// You don't have to change the parameters, they are only shown as an example.\n// select this new auth provider for your project (API Properties -> Authentication Provider)\n// You should now be able to log in (e.g. using Data Explorer) as e.g. einstein/password.\n\nfunction SimpleLDAPAuthProvider () {\n    var result = {};\n    var configSetup = {};\n\n    // This gets called first to pass in the required LDAP configuration values\n    result.configure = function configure(myConfig) {\n        configSetup.serverName = myConfig.serverName || \"ldap://ldap.forumsys.com\";\n        configSetup.keyLifetimeMinutes = myConfig.keyLifetimeMinutes || 60;\n    };\n\n    // This gets called to validate the user payload against LDAP service\n    result.authenticate = function authenticate(payload) {\n        java.lang.System.out.println(\"Authenticating with LDAP: \" + payload.username);\n        var authResponse = {\n            roleNames: [],\n            userIdentifier: payload.username,\n            keyLifetimeSeconds: configSetup.keyLifetimeMinutes,\n            userData: {},\n            // We hard-code userInfo here, but you can add information that will be returned\n            // to the authenticator in the response, along with the auth token.\n            userInfo: {typeOfPerson: 'Cool'},\n            // If you are writing an auth provider for use with the *admin* project,\n            // then you will need to include the accountIdent in the userInfo, e.g.:\n            // userInfo: {typeOfPerson: 'Cool', accountIdent: 1000},\n            // If you know when and from where this user last logged in, you can\n            // optionally return it here.\n            lastLogin : {\n                datetime: null,\n                ipAddress : null\n            }\n        };\n\n        // Set up the JNDI environment\n        var Hashtable = Java.type(\"java.util.Hashtable\");\n        var env = new Hashtable();\n        env.put(\"java.naming.factory.initial\", \"com.sun.jndi.ldap.LdapCtxFactory\");\n        env.put(\"java.naming.provider.url\", \"ldap://ldap.forumsys.com\");\n        var userCN = \"uid=\" + payload.username + \",dc=example,dc=com\";\n        env.put(\"java.naming.security.authentication\", \"simple\");\n        env.put(\"java.naming.security.principal\", userCN);\n        env.put(\"java.naming.security.credentials\", payload.password);\n        env.put(\"java.naming.referral\", \"follow\");\n\n        var InitialDirContext = Java.type(\"javax.naming.directory.InitialDirContext\");\n        try {\n            // First, can we connect? If not, then either the user is unknown,\n            // or the password is wrong, and this will throw an exception.\n            var ctx = null;\n\n            try {\n                ctx = new InitialDirContext(env);\n            }\n            catch (e) {\n                return {\n                    errorMessage: \"Unable to authenticate with LDAP server: \" + e.getMessage()\n                }\n            }\n\n            // Next, we retrieve the mail and phone attributes of the user, if present\n            var attrs = ctx.getAttributes(userCN, [\"mail\", \"telephoneNumber\"]);\n            var attrsEnum = attrs.getAll();\n            while (attrsEnum.hasMore()) {\n                var attrib = attrsEnum.next();\n                authResponse.userData[attrib.getID()] = attrib.get().toString();\n            }\n\n            // If there are multi-valued attributes, this is how to retrieve them\n            // attrs = ctx.getAttributes(userCN, [\"groups\"]);\n            // attrsEnum = attrs.getAll();\n            // if (attrsEnum.hasMore()) {\n            //     var mvAttr = attrsEnum.next();\n            //     var attrEnum = mvAttr.getAll();\n            //     while (attrEnum.hasMore()) {\n            //         var groupName = attrEnum.next();\n            //         // For example, we're only interested in groups starting with \"LAC-\"\n            //         if (groupName.startsWith(\"LAC-\")) {\n            //             authResponse.roleNames.push(groupName.substring(4));\n            //         }\n            //     }\n            // }\n\n            // Look for all the groups that contain our user. This is highly dependent\n            // on your LDAP schema -- many schemas store the memberships in the users.\n            var SearchControls = Java.type(\"javax.naming.directory.SearchControls\");\n            var ctls = new SearchControls();\n            ctls.setReturningAttributes([\"ou\"]);\n            var answer = ctx.search(\"dc=example,dc=com\",\n                    \"(&(objectClass=groupOfUniqueNames)(uniqueMember=\" + userCN + \"))\", ctls);\n            while (answer.hasMore()) {\n                var groupName = answer.next().getAttributes().get(\"ou\").get();\n                if (groupName == \"scientists\") {\n                    java.lang.System.out.println(\"User \" + payload.username +\n                        \" is a scientist and therefore gets full access.\");\n                    authResponse.roleNames.push(\"API Owner\");\n                    authResponse.roleNames.push(\"Full access\");\n                }\n                else if (groupName == \"mathematicians\") {\n                    java.lang.System.out.println(\"User \" + payload.username +\n                        \" is a mathematician and therefore gets read-only access.\");\n                    authResponse.roleNames.push(\"Read only\");\n                }\n                else {\n                    // Ignore other groups\n                    java.lang.System.out.println(\"Ignoring group: \" + groupName);\n                }\n            }\n\n            if ( ! authResponse.roleNames.length) {\n                java.lang.System.out.println(\"User \" + payload.username + \" gets no access\");\n                return {\n                    errorMessage: \"User \" + payload.username +\n                        \" is neither a mathematician nor a scientist: access denied.\"\n                }\n            }\n        }\n        catch (e) {\n            return {\n                errorMessage: e.getMessage()\n            }\n        }\n\n        return authResponse;\n    };\n\n    // getLoginInfo gets called to create the logon dialog - you can change this if your\n    // authentication method does not use username/password, or if you don't like the names,\n    // but of course you'll have to change the authenticate function correspondingly.\n    result.getLoginInfo = function getLoginInfo() {\n        return {\n            fields: [\n                {\n                    name: \"username\",\n                    display: \"User name\",\n                    description: \"Enter your Username, e.g. einstein\",\n                    type: \"text\",\n                    length: 40,\n                    helpURL: \"\"\n                },\n                {\n                    name: \"password\",\n                    display: \"Password\",\n                    description: \"Enter your password, e.g. password\",\n                    type: \"password\",\n                    length: 40,\n                    helpURL: \"\"\n                }\n            ],\n            links : []\n        };\n    };\n\n    // This function is called to determine what parameters can be configured in this auth provider.\n    // The values are stored in the admin database when the user saves the auth provider.\n    result.getConfigInfo = function getConfigInfo() {\n        return {\n            current : {\n                \"serverName\": configSetup.serverName,\n                \"keyLifetimeMinutes\" : configSetup.keyLifetimeMinutes\n            },\n            fields : [\n            {\n                name: \"serverName\",\n                display: \"LDAP Server Name\",\n                type: \"text\",\n                length: 40,\n                helpURL: \"\"\n            },\n            {\n                name: \"keyLifetimeMinutes\",\n                display: \"API Key Lifetime (Minutes)\",\n                type: \"number\",\n                length: 8,\n                helpURL: \"\"\n            }\n            ],\n            links: []\n        };\n    };\n\n    return result;\n}\n",
    "bootstrap_config_value": "create\n",
    "param_map": "\"keyLifetimeMinutes\"=\"60\", \"serverName\"=\"ldap://localhost:389\"",
    "get_config_info_code": null,
    "configure_code": null,
    "get_login_info_code": null,
    "auth_code": null,
    "account_ident": null
  },
  {
    "name": "GatewayAuth",
    "comments": null,
    "auth_type_ident": 3,
    "class_name": "com.kahuna.server.auth.HttpAuthProvider",
    "javascript_authprovider_code": null,
    "bootstrap_config_value": null,
    "param_map": null,
    "get_config_info_code": "var pkField = configInfo.addConfigField();\npkField.name = \"AuthPublicKeyText\";\npkField.description = \"The public key to verify incoming authentication. This can be either:\" +\n\t\t\"a JSON Web Key (JWK), which will start with something like { \\\"kty\\\": \\\"RSA\\\", etc...\" +\n\t\t\"an X.509 certificate, which will start with the line -----BEGIN CERTIFICATE-----,\" +\n\t\t\"or an X.509 public key, which will start with the line -----BEGIN PUBLIC KEY-----\";\npkField.display = \"Auth certificate/public key\";\npkField.length = 10000;\n\nvar pkAlgoField = configInfo.addConfigField();\npkAlgoField.name = \"AuthPublicKeyAlgorithm\";\npkAlgoField.description = \"Optional: the algorithm for the public key. If none is specified, RSA is assumed.\";\npkAlgoField.display = \"Auth public key algorithm\";\npkAlgoField.length = 20;\n\nvar jwtHeaderName = configInfo.addConfigField();\njwtHeaderName.name = \"AuthHeaderName\";\njwtHeaderName.description = \"Optional: name of HTTP header containing authentication, used for caching.\" +\n\t\t\" If this is not specified, no caching will occur, which will incur a small performance penalty (see docs for details)\";\njwtHeaderName.display = \"Auth Header Name\";\njwtHeaderName.length = 30;\n",
    "configure_code": "for (var paramName in parameters) {\n    log.info(\"Parameter \" + paramName +\n        \" has value \" + parameters[paramName]);\n}\n",
    "get_login_info_code": "loginInfo.loginStatus = \"Authentication is assumed to be done by the Gateway. Login is not allowed here.\";\n\t\n",
    "auth_code": "var encodedJwt = servletRequest.getHeader(\"Authorization\");\nif ( ! encodedJwt || encodedJwt.trim().length === 0) {\n    result.errorMessage = \"No Authorization header provided\";\n    return;\n}\nif ( ! encodedJwt.startsWith(\"Bearer \")) {\n    result.errorMessage = \"Authorization header does not have scheme Bearer\";\n    return;\n}\nencodedJwt = encodedJwt.substring(\"Bearer \".length);\nvar Jwts = Java.type(\"io.jsonwebtoken.Jwts\");\n// Parse and validate the JWT\nvar jwtClaims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(encodedJwt);\nresult.userIdentifier = jwtClaims.body.sub;\nresult.userData[\"name\"] = jwtClaims.body.name;\n/*\n * The data stored in userData object with e.g. key \"phoneNumber\" can be retrieved in rules,\n * events, functions, etc... with:\n * req.apiKey.getDataObjects().get(\"phoneNumber\");\n */\nvar groups = jwtClaims.body.groups;\nprint(\"result.userIdentifier:\" + result.userIdentifier +\n    \" result.userData:\" + result.userData +\n    \" groups:\" + groups ); // Outputs to console\nif (groups.indexOf('admin_staff') > -1) {\n    result.roleNames.add(\"Full access\");\n}\nelse {\n    result.errorMessage = \"Only managers can use this API\";\n}\n",
    "account_ident": null
  },
  {
    "name": "Built-in authentication",
    "comments": null,
    "auth_type_ident": 1,
    "class_name": "com.kahuna.server.auth.db.DefaultAuthProvider",
    "javascript_authprovider_code": null,
    "bootstrap_config_value": null,
    "param_map": "\"datasource\"=\"AdminDB\"",
    "get_config_info_code": null,
    "configure_code": null,
    "get_login_info_code": null,
    "auth_code": null,
    "account_ident": null
  }
]