async_nats/
auth_utils.rs

1// Copyright 2020-2022 The NATS Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use nkeys::KeyPair;
15use once_cell::sync::Lazy;
16use regex::Regex;
17use std::{io, path::Path};
18
19/// Loads user credentials file with jwt and key. Return file contents.
20/// Uses tokio non-blocking io
21pub(crate) async fn load_creds(path: &Path) -> io::Result<String> {
22    tokio::fs::read_to_string(path).await.map_err(|err| {
23        io::Error::new(
24            io::ErrorKind::Other,
25            format!("loading creds file '{}': {}", path.display(), err),
26        )
27    })
28}
29
30/// Parses the string, expected to be formatted as credentials file
31pub(crate) fn parse_jwt_and_key_from_creds(contents: &str) -> io::Result<(&str, KeyPair)> {
32    let jwt = parse_decorated_jwt(contents).ok_or_else(|| {
33        io::Error::new(
34            io::ErrorKind::InvalidData,
35            "cannot parse user JWT from the credentials file",
36        )
37    })?;
38
39    let nkey = parse_decorated_nkey(contents).ok_or_else(|| {
40        io::Error::new(
41            io::ErrorKind::InvalidData,
42            "cannot parse nkey from the credentials file",
43        )
44    })?;
45
46    let kp =
47        KeyPair::from_seed(nkey).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
48
49    Ok((jwt, kp))
50}
51
52// This regex parses a credentials file.
53//
54// The credentials file is typically
55// `~/.nkeys/creds/synadia/<account/<account>.creds` and looks like this:
56//
57// ```
58// -----BEGIN NATS USER JWT-----
59// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5...
60// ------END NATS USER JWT------
61//
62// ************************* IMPORTANT *************************
63// NKEY Seed printed below can be used sign and prove identity.
64// NKEYs are sensitive and should be treated as secrets.
65//
66// -----BEGIN USER NKEY SEED-----
67// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
68// ------END USER NKEY SEED------
69// ```
70static USER_CONFIG_RE: Lazy<Regex> = Lazy::new(|| {
71    Regex::new(r"\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))")
72        .unwrap()
73});
74
75/// Parses a credentials file and returns its user JWT.
76fn parse_decorated_jwt(contents: &str) -> Option<&str> {
77    let capture = USER_CONFIG_RE.captures_iter(contents).next()?;
78    Some(capture.get(1)?.as_str())
79}
80
81/// Parses a credentials file and returns its nkey.
82fn parse_decorated_nkey(contents: &str) -> Option<&str> {
83    let capture = USER_CONFIG_RE.captures_iter(contents).nth(1)?;
84    Some(capture.get(1)?.as_str())
85}