1 /**
2 * Copyright © DiamondMVC 2019
3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module diamond.security.csrf;
7 
8 import diamond.core.apptype;
9 
10 static if (isWeb)
11 {
12   import diamond.http;
13   import diamond.errors.checks;
14 
15   /// The key of the token's storage in the session.
16   private static const __gshared CSRFTokenKey = "__D_CSRFTOKEN";
17 
18   /**
19   * Generates a CSRF token.
20   * Params:
21   *   client = The client.
22   * Returns:
23   *   The generated csrf token. If a token already exist, the existing token is returned.
24   */
25   string generateCSRFToken(HttpClient client)
26   {
27     auto token = client.session.getValue!string(CSRFTokenKey);
28 
29     if (token)
30     {
31       return token;
32     }
33 
34     import diamond.security.tokens.generictoken;
35 
36     token = genericToken.generate()[0 .. 64];
37 
38     client.session.setValue(CSRFTokenKey, token);
39 
40     return token;
41   }
42 
43   /**
44   * Clears the csrf token.
45   * Params:
46   *   client =  The client.
47   */
48   void clearCSRFToken(HttpClient client)
49   {
50     client.session.removeValue(CSRFTokenKey);
51   }
52 
53   /**
54   * Checks whether a token is a valid csrf token for the request.
55   * Params:
56   *   client =     The client.
57   *   token =       The token to validate.
58   *   removeToken = Boolean determining whether the token should be cleared after validation.
59   * Returns:
60   *   Returns true if the token is valid, false otherwise.
61   */
62   bool isValidCSRFToken
63   (
64     HttpClient client,
65     string token, bool removeToken
66   )
67   {
68     enforce(token && token.length == 64, "Invalid csrf token.");
69 
70     auto csrfToken = client.session.getValue!string(CSRFTokenKey);
71 
72     if (csrfToken && removeToken)
73     {
74       client.session.removeValue(CSRFTokenKey);
75     }
76 
77     return csrfToken !is null && token == csrfToken;
78   }
79 }