DevLearningTools

MODULE 14 · LESSON 05

Authentication Basics

Verifying who's calling your ColdFusion application before letting them in: the built-in cflogin framework for web apps, and reading an Authorization header with getHTTPRequestData() to protect a REST endpoint.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

The last few lessons built and consumed REST APIs and returned JSON, but never once asked "who's actually calling this?" Authentication is the step that answers that: verifying the caller's identity before your code does anything with their request. ColdFusion actually ships two genuinely different tools for this, depending on what you're protecting: a built-in login framework (cflogin) for a traditional web app, and manual header-reading for a REST API, since an API caller has no session or login form to redirect to.

Learning Objectives

After completing this lesson, you'll be able to:

  • Explain the difference between authenticating a web app user (cflogin) and authenticating an API caller (headers).
  • Use cflogin, cfloginuser, and cflogout to build a basic login flow.
  • Check login state and roles with isUserLoggedIn(), getAuthUser(), and isUserInRole().
  • Read a custom Authorization header with getHTTPRequestData() to protect a REST endpoint.
  • Return a proper 401 response, with cfheader, when a caller isn't authenticated.

Two Different Problems, Two Different Tools

Web app login (cflogin)Session-based
  • A person fills out a login form in a browser
  • ColdFusion tracks them with a session/cookie afterward
  • isUserLoggedIn() answers "are they still logged in?" on every later page
  • Built into the CFML language itself
API authentication (headers)Stateless
  • A program (not a human) sends a request with credentials attached
  • No browser, no session, no login form to redirect to
  • Every single request carries its own proof of identity, in a header
  • You read and check that header yourself

How API Authentication Actually Works

Request Arrives

with an Authorization header

Read the Header

getHTTPRequestData()

Valid?

compare against expected value

Process or Reject

continue, or return 401

cflogin, cfloginuser, and cflogout

cflogin is a container that only runs its code when the current user isn't already logged in, exactly where a login form's processing logic belongs. cfloginuser is called inside it once credentials check out, registering the user (and their roles) with ColdFusion's own login framework for the rest of the session.

Tag Syntax
<cflogin>
    <cfif not isDefined("cflogin")>
        <!--- No credentials submitted yet, show a login form --->
        <cfoutput>Please log in.</cfoutput>
        <cfabort>
    </cfif>

    <!--- Look up the submitted username/password against your own data source --->
    <cfquery name="qUser" datasource="myDataSource">
        SELECT username, role
        FROM users
        WHERE username = <cfqueryparam value="#cflogin.name#" cfsqltype="cf_sql_varchar">
        AND passwordHash = <cfqueryparam value="#hash(cflogin.password)#" cfsqltype="cf_sql_varchar">
    </cfquery>

    <cfif qUser.recordCount EQ 1>
        <cfloginuser name="#cflogin.name#" password="#cflogin.password#" roles="#qUser.role#">
    <cfelse>
        <cfoutput>Invalid username or password.</cfoutput>
        <cfabort>
    </cfif>
</cflogin>
CFScript
cflogin {
    if (!isDefined("cflogin")) {
        writeOutput("Please log in.");
        abort;
    }

    qUser = queryExecute(
        "SELECT username, role FROM users WHERE username = :username AND passwordHash = :passwordHash",
        { username: cflogin.name, passwordHash: hash(cflogin.password) }
    );

    if (qUser.recordCount == 1) {
        cfloginuser(name = cflogin.name, password = cflogin.password, roles = qUser.role[1]);
    } else {
        writeOutput("Invalid username or password.");
        abort;
    }
}

// Later, to end the session:
cflogout();
NOTE

cflogin.name and cflogin.password are populated automatically from a submitted form's j_username/j_password fields (or HTTP Basic Auth credentials), ColdFusion wires that part up for you.

Checking Login State Later

Once cfloginuser has run, three functions cover almost everything a page needs to know about the current user, for the rest of that session.

CFScript
if (isUserLoggedIn()) {
    currentUser = getAuthUser();
    if (isUserInRole("admin")) {
        // show admin-only content
    }
} else {
    // redirect to login
}

Protecting a REST Endpoint With getHTTPRequestData()

A REST API caller has no session and no login page to see, so cflogin doesn't apply here. Instead, every request is expected to carry its own proof of identity, commonly in an Authorization header, and your code reads and checks it directly with getHTTPRequestData().

CFScript
requestData = getHTTPRequestData();
authHeader = requestData.headers["Authorization"] ?: "";

if (authHeader != "Bearer " & application.expectedApiKey) {
    cfheader(statuscode = 401);
    cfheader(name = "WWW-Authenticate", value = "Bearer");
    cfcontent(type = "application/json", reset = true);
    writeOutput(serializeJSON({ "error": "Unauthorized" }));
    abort;
}

// Credentials are valid, continue handling the request normally
NOTE

getHTTPRequestData() returns a struct with headers, content, method, and protocol keys, headers is itself a struct you read by name, exactly like any other CFML struct.

Common Beginner Mistakes

Trying to use cflogin to protect a REST API

cflogin expects a browser session and a login form to fall back to, neither of which a REST caller has. API requests need their own credentials checked on every single request, typically by reading a header with getHTTPRequestData().

Comparing a submitted password directly against a stored plaintext password

Passwords should never be stored in plaintext. hash() (or a proper password-hashing function) belongs on both the stored value and the submitted one before comparing, covered in more depth in the upcoming Security module.

Forgetting that getHTTPRequestData() header keys can vary in case

HTTP headers are technically case-insensitive, but struct key lookups in CFML are not guaranteed to match differing case automatically depending on engine and settings, checking for the header name exactly as the client actually sends it avoids this trap.

Not returning a proper 401 status on failed API authentication

A REST client's code typically branches on status codes. Returning 200 with an error message buried in the JSON body, instead of a real cfheader(statuscode=401), makes failures much harder for calling code to detect correctly.

Best Practices

  • Use cflogin/cfloginuser for traditional session-based web app pages, not for REST APIs.
  • Read and check an Authorization (or similar) header with getHTTPRequestData() for every REST request that needs protecting.
  • Return a real 401 status via cfheader when authentication fails, not a 200 with an error message in the body.
  • Never compare or store plaintext passwords, hash them on both sides of the comparison.

Interview Questions

What's the difference between cflogin-based authentication and API key/header-based authentication?

cflogin assumes a browser session: a user logs in once, and ColdFusion remembers them for the rest of that session via isUserLoggedIn(). A REST API caller has no session to remember, so every single request carries its own credentials, typically in a header, checked manually with getHTTPRequestData() rather than through cflogin.

What does getHTTPRequestData() actually return?

A struct with four keys: headers (a struct of the request's HTTP headers), content (the raw request body), method (the HTTP verb, equivalent to cgi.request_method), and protocol. Reading a specific header, like Authorization, means indexing into the headers struct by name.

How do you return a 401 Unauthorized response from a ColdFusion REST endpoint?

cfheader(statuscode=401), optionally paired with a WWW-Authenticate header describing the expected auth scheme, followed by writing whatever error body the API contract expects (commonly a small JSON object) via cfcontent and writeOutput/writeDump.

Summary

In this lesson, you covered the two genuinely different authentication problems ColdFusion handles: session-based web app login with cflogin/cfloginuser/cflogout plus isUserLoggedIn()/getAuthUser()/isUserInRole(), and stateless REST API authentication by reading an Authorization header with getHTTPRequestData() and returning a proper 401 via cfheader when it doesn't check out.

What's Next?

The next lesson covers cfhttp: making outbound HTTP requests from ColdFusion, including sending the very same kind of authentication headers you just learned to check for.